快速排序-java

快排: 有两个指针left,right, 初值为数据表的最左端位置, 最右端位置, 设置基准点(pivot,或者称为分区点)

快排是一个原地,不稳定的排序算法

实现思路:

1.从right所指向的位置开始向左边搜索 ,找到的第一个小于基准值(pivotKey)的记录,就与基准点(pivot,基准值的位置)进行交换;
2.再从left所指向的位置向右边搜索,找到的第一个大于基准值的记录,就与基准点进行交换;
3.重复步骤 1,2 直到 left = right,完成一趟遍历
完成一趟遍历后: 获得一个基准值在该数据表的最终位置, 之后就不用管这个基准值了,直接对其他的元素重复1,2,3步骤即可,最后得到的就是一个有序的数据表

时间复杂度: \(最好 : O(NlogN),最坏: O(N^2),平均 : O(NlogN)\)

空间复杂度: \(O(1)\)

优化思路

使用三数取中法 , 在 非递归实现中 已经体现

递归实现


package sort.quicksort;


import java.util.Arrays;

// 快排 递归实现
public class QuickSort1 {

    /**
     * @param target 需要排序的数据表
     * @param left  数据表的最左角标
     * @param right 数据表的最右角标
     */
    public static void quickSort(int[] target ,int left,int right){
        int i = left;
        int j = right;
        int p = left; // 记录基准值的位置
        int pivotKey = target[p]; // 基准值
        // 每一次循环都会查找一次基准值的位置
        while (i <= j){
            // 从右端查找小于基准值的 数据, 如果查找到了,跳出循环
            while ( j >= p && target[j] >= pivotKey){
                j--;
            }
            // 将从右端查找到的小于基准值的数据,与放入数据表左端
            if (j >= p){
                target[p] = target[j];
                p = j;
            }
            /* 问:  为什么要有下面两个if 判断呢? 没有行不行呢?
             答: 是为了保证 我们每次循环结束的时候,保证 p都在左端, 当递归执行的时候,所有的搜索都是从 右端开始的搜索的.
              如果 没有下面两个判断, 当p 指向的是表的右端的时候, 我们就要从 数据表的左端开始搜索数据,
              即没有 下面两个if, 就在造成 p在左端,而又从左端开始搜索数据 , 这明显是错误的搜索方式
            */


            // 从左端查找大于基准值的数据, 如果查到了,则 向右偏移
            if (i <= p && target[i] <= pivotKey){
                i++;
            }
            // 如果从左端查找到的大于基准值的数据, 则放入数据表右端
            if (i <= p){
                target[p] = target[i];
                p = i;
            }
            System.out.println(Arrays.toString(target));
        }
        // 一趟遍历后, 找到一个基准值的最终位置
        target[p] = pivotKey;
        System.out.println(Arrays.toString(target));
        System.out.println();
        // 对于低子表进行递归排序
        if (p - left >1){
            quickSort(target,left,p-1);
        }
        // 对高子表进行递归排序
        if (right-p >1){
            quickSort(target,p+1,right);
        }
    }

    /**
     * @param target 要进行排序的数据表
     */
    public static void QuickSort(int[] target){
        quickSort(target,0,target.length-1);
    }

    public static void main(String[] args) {
        int[] arr = {49,38,65,97,76,13,27,49};
        System.out.println(Arrays.toString(arr));
        System.out.println();
        QuickSort(arr);
        System.out.println();
        System.out.println(Arrays.toString(arr));
    }
}

非递归实现

package sort.quicksort;

import java.util.Arrays;
import java.util.Stack;

// 快排,非递归
public class QuickSort2 {
    /**
     * @param target 要排序的目标数组
     */
    public static void quickSort(int[] target) {
        int left = 0;
        int right = target.length-1;
        int pivotLoc; // 基准点 的位置
        if (left >= right)
            return;
        Stack<Integer> stack = new Stack<>();
        stack.push(left);
        stack.push(right);
        while (!stack.empty()) {
            // 先弹出right,再弹出left
            right = stack.pop();
            left = stack.pop();
            // 寻找出基准元素的位置
            pivotLoc = partition(target, left, right);
            // 先压left,再压 right,
            if (pivotLoc - left > 1) {
                stack.push(left);
                stack.push(pivotLoc - 1);
            }
            if (right - pivotLoc > 1) {
                stack.push(pivotLoc + 1);
                stack.push(right);
            }
        }
    }

    /**
     * @param target 子数组
     * @param left 左端位置
     * @param right 右端位置
     * @return 返回基准值 pivot的插入位置
     */
    public static int partition(int[] target, int left, int right) {
         setPivot(target,left,right); // 使用三数取中法

        int pivotKey = target[left]; // 用第一个元素作为基准元素
        while (left < right) { // 两侧交替向中间扫描
            while (left < right && target[right] >= pivotKey)
                right--;
            target[left] = target[right]; // 将比基准值小的数据移至基准值的左端
            while (left < right && target[left] <= pivotKey)
                left++;
            target[right] = target[left]; // 将比基准值大的数据移至基准值的右端
        }
        target[left] = pivotKey; // 在中间位置放回基准值
        return left; // 返回基准元素所在位置
    }

    // 算法优化 : 三数取中
    public static void setPivot(int[] target, int left,int right){
        // 三数取中,将中间元素放在第一个位置
        if (target[left] > target[right]){
            swap(target, left, right);
        }
        if (target[(left+ right)/ 2] > target[right]){
            swap(target, (left+ right)/ 2, right);
        }
        if (target[left] < target[(left+ right)/ 2]){
            swap(target, (left+ right)/ 2, left);
        }
    }

    private static void swap(int[] target, int left, int right) {
        int temp = target[left];
        target[left] = target[right];
        target[right] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {49,38,65,97,76,13,27,49};
        System.out.println(Arrays.toString(arr));
        System.out.println();
        quickSort(arr);
        System.out.println();
        System.out.println(Arrays.toString(arr));
    }
}

posted @ 2021-11-09 22:45  背影g  阅读(80)  评论(0)    收藏  举报