menu 冷 の Codeworld
search self_improvement
目录

通透百万数据秒级排序 : 快速排序

冷环渊
冷环渊 2022年04月04日  ·  阅读 558

快速排序

快速排序 是随机选中其中一个元素,进行元素筛选 一遍一遍将小于这个元素的放在左边,把大于元素的放在右边

之后两边再分别随机选择出来一个而元素继续上述操作

  • 选出对比元素,可以固定可以随机 可能会影响效率
  • 小的放在左边,大的放在右边
  • 持续递归
  • 拼接返回

动态图

查看源图像

理解实例

简易方便理解版本的快排

利用集合来演示快排

这个效率不会很好,但是很适合我们来理解快速排序的思路

    public static ArrayList<Integer> quickSort(ArrayList<Integer> arr) {
        if (arr == null || arr.size() <= 1) {
            return arr;
        }
        int leader = arr.get(0);
        //存放左右的结果
        ArrayList<Integer> left = new ArrayList<>();
        ArrayList<Integer> right = new ArrayList<>();
        for (int i = 1; i < arr.size(); i++) {
            //根据左右条件添加
            if (arr.get(i) <= leader) {
                left.add(arr.get(i));
            } else {
                right.add(arr.get(i));
            }
        }
        //递归
        ArrayList<Integer> leftResult = quickSort(left);
        ArrayList<Integer> rightResult = quickSort(right);
        //    合并
        ArrayList<Integer> result = new ArrayList<>();
        result.addAll(leftResult);
        result.add(leader);
        result.addAll(rightResult);
        return result;
    }

标准版本

标准版的思路和简易版本有一定区别

这里是有两个指针一个指向前面 一个指向后面。根据选出的值来从右往左找小于这个数的值移动,之后从左边找大于值的数移动到右边,每一次交换两个数分别放在左右两边,直到左右指针相遇 就将这个数值放在相遇点,直到有序为止。

图解

image-20220404001420036

image-20220404001528964

image-20220404001709689

image-20220404001753252

重复这个过程一直到有序为止,

我们只需要编写一个定位方法,第一次就按照上图来讲low位置的左右两边交换好

之后只需要出去中位,左右递归就可以排序好数列了,至此百万数字秒级排序的快速排序思路完成

代码实现

package com.hyc.algorithm.sort;

import java.util.ArrayList;

/**
 * @projectName: DataStructure
 * @package: com.hyc.algorithm.sort
 * @className: QuickSort
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/4/3 23:30
 * @version: 1.0
 */
public class QuickSort {
    public static void main(String[] args) {
        //ArrayList<Integer> arr = new ArrayList<>();
        //arr.add(3);
        //arr.add(1);
        //arr.add(9);
        //arr.add(2);
        //arr.add(6);
        //arr.add(8);
        //arr.add(7);
        //arr.add(5);
        //System.out.println(quickSort(arr));
        //int[] arr = {3, 1, 9, 2, 6, 8, 7, 5};
        int[] test = new int[8000000];
        //测试数据
        for (int i = 0; i < 8000000; i++) {
            test[i] = (int) (Math.random() * 800000);
        }
        long time = System.currentTimeMillis();
        quickSort(test);
        long end = System.currentTimeMillis();
        long t = ((end - time) / 1000);
        System.out.println("一共用时 " + t + "秒");
    }


    //简易版本
    public static ArrayList<Integer> quickSort(ArrayList<Integer> arr) {
        if (arr == null || arr.size() <= 1) {
            return arr;
        }
        int leader = arr.get(0);
        //存放左右的结果
        ArrayList<Integer> left = new ArrayList<>();
        ArrayList<Integer> right = new ArrayList<>();
        for (int i = 1; i < arr.size(); i++) {
            if (arr.get(i) <= leader) {
                left.add(arr.get(i));
            } else {
                right.add(arr.get(i));
            }
        }
        //递归
        ArrayList<Integer> leftResult = quickSort(left);
        ArrayList<Integer> rightResult = quickSort(right);
        //    合并
        ArrayList<Integer> result = new ArrayList<>();
        result.addAll(leftResult);
        result.add(leader);
        result.addAll(rightResult);
        return result;
    }


    //标准版快速排序
    public static void quickSort(int[] arr) {
        quickSort(arr, 0, arr.length - 1);
    }

    public static void quickSort(int[] arr, int low, int high) {
        if (low >= high) {
            return;
        }
        //已经交换过一次 中间值的为止不会变化了
        int partPoint = partition(arr, low, high);
        //递归左边
        quickSort(arr, low, partPoint - 1);
        //递归右边
        quickSort(arr, partPoint + 1, high);
    }

    //进行一次左右换位
    public static int partition(int[] arr, int low, int high) {
        int leader = arr[low];
        while (low < high) {
            while (low < high && arr[high] >= leader) {
                //从右边寻找第一个小于leader的数字
                high--;
            }
            //    交换值
            arr[low] = arr[high];

            while (low < high && arr[low] <= leader) {
                //从左边找到第一个大于leader的数字
                low++;
            }
            arr[high] = arr[low];
        }
        arr[low] = leader;
        return low;
    }
}

效率版

思路和标准版一致

//速度优化之后的快速排序
public static void optimizeQuickSort(int[] arr, int low, int high) {
    int l = low;
    int r = high;
    int pivot = arr[l + r / 2];
    int temp = 0;
    while (l < r) {
        while (arr[l] < pivot) {
            l += 1;
        }
        while (arr[r] > pivot) {
            r -= 1;
        }
        if (l >= r) {
            break;
        }

        temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;

        if (arr[l] == pivot) {
            r -= 1;
        }
        if (arr[r] == pivot) {
            l += 1;
        }
    }
    //防止栈溢出
    if (l == r) {
        l += 1;
        r -= 1;
    }

    //防止栈溢出,当low指针0 r = 0 的时候就不许要排序递归左边了,
    if (low < r) {
        quickSort(arr, low, r);
    }
    //当 高位大于 左边指针的时候 就需要向右边递归了
    if (high > l) {
        quickSort(arr, l, high);
    }
}