Java实现快速排序

原理:

  1. 从待排序区间选择一个数,作为基准值(par);
  2. Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
  3. 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。

平均时间复杂度为O(nlogn)
空间复杂度O(log(n)) ~ O(n)
在这里插入图片描述
在这里插入图片描述
递归版本思路:
left >= right的时候就结束递归
先找到par
然后递归(left,par-1);
递归(par+1,right);

实现代码:

import java.util.Arrays;

/**
 * @author PineappleSnow
 * @version 1.0
 * @date 2020/6/5 15:36
 *
 * 基础版本快排
 */
public class QuickSort {

    public static void quickSort(int[] array) {
        quick(array,0,array.length-1);
    }

    public static void quick(int[] array, int left, int right) {
        if (left >= right) return;
        int par = partition(array, left, right);
        quick(array, left, par-1);//递归基准左边
        quick(array, par+1, right);//递归基准右边
    }

    public static int partition(int[] array, int low, int high) {
        int tmp = array[low];//找到基准
        while (low < high) {//循环终止条件
            while (low < high && array[high] >= tmp) {//后面没找到小于基准的时候,high--,就是后面的大于基准
                high--;
            }
            array[low] = array[high];//循环终止说明找到小于基准的值了,把它放到前面
            while (low < high && array[low] <= tmp) {//前面没找到大于基准的时候,low++,就是前面的小于基准
                low++;
            }
            array[high] = array[low];//循环终止说明找到大于基准的值了,把它放到后面
        }
        array[low] = tmp;//把 tmp 的值放回到 low 和 high 相遇的地方
        return low;
    }

    public static void main(String[] args) {
        int[] array = {9,8,7,6,5,4,3,2,1};
        System.out.println(Arrays.toString(array));
        quickSort(array);
        System.out.println(Arrays.toString(array));
    }
}

非递归版本思路:
需要用栈来保存左右两边
求出par(left,par-1)为左边 (par+1,right)为右边
par和左边只有一个元素的时候,或者par左边没有元素,就不用往栈里放了
par和右边只有一个元素的时候,或者par右边没有元素,就不用往栈里放了
栈空结束循环

实现代码:

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

/**
 * @author PineappleSnow
 * @version 1.0
 * @date 2020/6/6 0:02
 *
 * 非递归实现快排
 */
public class QuickSort3 {

    public static void quickSort(int[] array) {
        Stack<Integer> stack = new Stack<>();
        int left = 0;
        int right = array.length - 1;
        int par = partition(array,left,right);
        if (par > left + 1) {
            stack.push(left);
            stack.push(par-1);
        }
        if (par < right - 1) {
            stack.push(par+1);
            stack.push(right);
        }
        while (!stack.empty()) {
            right = stack.pop();
            left = stack.pop();
            par = partition(array,left,right);
            if (par > left + 1) {
                stack.push(left);
                stack.push(par-1);
            }
            if (par < right - 1) {
                stack.push(par+1);
                stack.push(right);
            }
        }
    }

    public static int partition(int[] array, int low, int high) {
        int tmp = array[low];
        while (low < high) {
            while (low < high && array[high] >= tmp) {
                high--;
            }
            array[low] = array[high];
            while (low < high && array[low] <= tmp) {
                low++;
            }
            array[high] = array[low];
        }
        array[low] = tmp;
        return low;
    }

    public static void main(String[] args) {
        int[] array = {9,8,7,6,5,4,3,2,1};
        System.out.println(Arrays.toString(array));
        quickSort(array);
        System.out.println(Arrays.toString(array));
    }
}



快排的几种优化:
1.待排序区间小于一个阈值时(例如 48),使用直接插入排序

原因:对于很小和部分有序的数组,快排不如插排好。当待排序序列的长度分割到一定大小后,继续分割的效率比插入排序要差,此时可以使用插排而不是快排

截止范围:待排序序列长度N = 10,虽然在5~20之间任一截止范围都有可能产生类似的结果,这种做法也避免了一些有害的退化情形。摘自《数据结构与算法分析》Mark Allen Weiness 著

if (high - low + 1 < 10)
{
	InsertSort(arr,low,high);
	return;
}//else时,正常执行快排

2.选择基准值很重要,通常使用三数取中法

避免有的时候数据是有序的,把每个数据都当做基准进行比较,通过三数取中法可以有效避免。
使优化后达到这样的效果array[mid] <= array[left] <= array[right]

分析:最佳的划分是将待排序的序列分成等长的子序列,最佳的状态我们可以使用序列的中间的值,也就是第N/2个数。可是,这很难算出来,并且会明显减慢快速排序的速度。这样的中值的估计可以通过随机选取三个元素并用它们的中值作为枢纽元而得到。事实上,随机性并没有多大的帮助,因此一般的做法是使用左端、右端和中心位置上的三个元素的中值作为枢纽元。显然使用三数中值分割法消除了预排序输入的不好情形,并且减少快排大约14%的比较次数

优化一:

import java.util.Arrays;

/**
 * @author PineappleSnow
 * @version 1.0
 * @date 2020/6/5 15:44
 *
 * 通过结合插入排序进行优化
 */
public class QuickSort1 {

    public static void quickSort(int[] array) {
        quick(array,0, array.length-1);
    }

    /**
     * 如果需要排序的区间小于100 那么进行插入排序 减少递归的时间空间复杂度
     * @param array
     * @param left
     * @param right
     */
    public static void quick(int[] array, int left, int right) {
        if (left >= right) return;
        if (right-left+1 < 100) {
            insertSort(array,left,right);
            return;
        }
        int par = partition(array, left, right);
        quick(array, left, par-1);
        quick(array, par+1, right);
    }

    public static int partition(int[] array, int low, int high) {
        int tmp = array[low];
        while (low < high) {
            while (low < high && array[high] >= tmp) {
                high--;
            }
            array[low] = array[high];
            while (low < high && array[low] <= tmp) {
                low++;
            }
            array[high] = array[low];
        }
        array[low] = tmp;
        return low;
    }

    public static void insertSort(int[] array, int left, int right) {
        for (int i = left+1; i <= right; i++) {
            int j;
            int tmp = array[i];
            for (j = i-1; j>= 0; j--) {
                if (array[j] > tmp) {
                    array[j+1] = array[j];
                }else {
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }

    public static void main(String[] args) {
        int[] array = {9,8,7,6,5,4,3,2,1};
        System.out.println(Arrays.toString(array));
        quickSort(array);
        System.out.println(Arrays.toString(array));
    }
}

优化二:

import java.util.Arrays;
import java.util.Random;

/**
 * @author PineappleSnow
 * @version 1.0
 * @date 2020/6/5 15:56
 *
 * 通过结合插入排序 + 三数取中 进行优化
 */
public class QuickSort2 {

    public static void quickSort(int[] array) {
        quick(array,0,array.length-1);
    }

    public static void quick(int[] array, int left, int right) {
        if (left >= right) return;

        //插入排序优化
        if (right- left + 1 < 100) {
            insertSort(array,left,right);
            return;
        }

        //三数取中优化
        three_num_mid(array,left,right);

        int par = partition(array, left, right);
        quick(array, left, par-1);
        quick(array, par+1, right);
    }

    public static int partition(int[] array, int low, int high) {

        int tmp = array[low];
        while (low < high) {
            while (low < high && array[high] >= tmp) {
                high--;
            }
            array[low] = array[high];
            while (low < high && array[low] <= tmp) {
                low++;
            }
            array[high] = array[low];
        }
        array[low] = tmp;
        return low;
    }

    public static void three_num_mid(int[] array, int left, int right) {
        //达到这样的效果
        //array[mid] <= array[left] <= array[right]
        int mid = (right + left) / 2;
        if (array[mid] > array[left]) {
            swap(array,left,mid);
        }
        if (array[left] > array[right]) {
            swap(array,left,right);
        }
        if (array[mid] > array[right]) {
            swap(array,mid,right);
        }
    }

    public static void insertSort(int[] array, int left, int right) {
        for (int i = left; i <= right; i++) {
            int j;
            int tmp = array[i];
            for (j = i - 1; j >= 0; j--) {
                if (array[j] > tmp) {
                    array[j+1] = array[j];
                }else {
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

    public static void swap(int[] array, int a, int b) {
        int tmp = array[a];
        array[a] = array[b];
        array[b] = tmp;
    }

    public static void main(String[] args) {
        int[] array = new int[10_0000];
        Random random = new Random();
        for (int i = 0; i < array.length; i++) {
            //有序数据
            array[i] = i;
            //无序数据
            //array[i] = random.nextInt(10_0000);
        }
        long start = System.currentTimeMillis();
        quickSort(array);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    public static void main1(String[] args) {
        int[] array = {9,8,7,6,5,4,3,2,1};
        System.out.println(Arrays.toString(array));
        quickSort(array);
        System.out.println(Arrays.toString(array));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值