排序算法-快排详细讲解(QuickSort)

快排简单介绍

快速排序作为经典的排序算法之一,其实也用到了冒泡排序的思想,冒泡排序是每一轮将最大(最小)的元素放到一端,而快速排序是每一轮找出比基准元素大、小的所有元素,然后放在基准元素的两边。是分治法体现之一。

单边循环法

只从数组的一边对元素进行遍历和交换。首先选定基准元素pivot,同时,设置一个mark指针指向数列起始位置,这个mark指针代表 小于基准元素的区域边界

  1. 从基准元素的下一个位置开始遍历数组
  2. 如果遍历到元素大于基准元素,就继续往后遍历
  3. 如果遍历到元素小于基准元素,则需要做两件事:第一mark++,增加区域边界;第二 交换最新遍历到元素和mark指针所在位置的元素进行交换。

代码演示 

/**
     *
     * 单边循环法
     *
     */
    public static  void quickSort(int [] arr,int startIndex,int endIndex){
        //递归结束条件:startIndex>=endIndex
        if(startIndex>=endIndex){
            return;
        }
        //得到基准元素位置
        int pivotIndex=partition(arr,startIndex,endIndex);

        quickSort(arr,startIndex,pivotIndex-1);
        //根据基准元素,分成两部分进行递归排序
        quickSort(arr,pivotIndex+1,endIndex);
    }

    /**
     * 分治(单边循环法)
     * @param arr 待交换的数组
     * @param startIndex 起始下标
     * @param endIndex 结束下标
     * @return
     */
    private static int partition(int [] arr,int startIndex,int endIndex){
        //取第一个位置(也可以选择随机位置)的元素作为基准元素
        int pivot=arr[startIndex];
        int mark=startIndex;

        for (int i=startIndex+1;i<=endIndex;i++){
            if (arr[i]<pivot){
                mark++;
                int p=arr[mark];
                arr[mark]=arr[i];
                arr[i]=p;
            }
        }
        //元素1和mark指针所在的位置的元素交换,因为元素1归属于小于pivot区域
        arr[startIndex]=arr[mark];
        //最后把pivot元素交换到mark指针所在位置,这一轮宣告结束
        arr[mark]=pivot;
        //返回
        return mark;
    }

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

双边循环法

双边循环法的思路是:先从right指针开始,right指向的元素如果大于或等于基准元素,则该元素不动(right指针的作用是保证它指向的元素以及它右边的元素都是大于基准元素的),然后right指针向左移动一位(进行比较下一个);如果right指针指向的元素比基准元素小,则right指针停止移动(right指针一直指向这个元素,做一个标识,直到left指针移动到比基准元素大的数字位置,然后和right指针进行交换),然后切换到left指针,如果left指向的元素小于或等于基准元素,则该元素不动,left指针继续向右移动,寻找下一个元素,如果指向的元素比基准元素大,则和right指针指向的元素进行元素交换(实现比基准元素小的数字放在它的左边,大的数字放在它的右边),然后right指针向左移动一位(之所以left指针不向右移动,是因为如果left和right指针相邻,二者同时移动,会出现right指针在left指针左边的情况),之后再从right指针开始新的一轮比较。

/**
     * 分治(双边循环法)
     * @param arr 待交换的数组
     * @param startIndex 起始下标
     * @param endIndex 结束下标
     * @return
     */
    public static  void quickSort(int [] arr,int startIndex,int endIndex){

        if (startIndex >= endIndex) {
            return;
        }
        int pivotIndex=partition(arr,startIndex,endIndex);

        quickSort(arr,startIndex,pivotIndex-1);
        quickSort(arr,pivotIndex+1,endIndex);
    }

   
    public static  int partition(int [] arr,int startIndex,int endIndex){
        //取第一个位置(也可以选择随机位置)的元素作为基准元素
        int pivot =arr[startIndex];
        int left=startIndex;
        int right=endIndex;

        while(left!=right){
            //控制right指针比较并左移
            while(left<right && arr[right]>pivot) right--;
            //控制left指针并右移
            while(left<right && arr[left]<=pivot) left++;
            //交换left和right指针所指的元素
            if (left<right){
                int temp=arr[left];
                arr[left] =arr[right];
                arr[right]=temp;
            }
        }
        //pivot 和指针重合点交换
        arr[startIndex] =arr[left];
        arr[left]=pivot;

        return left;
    }

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

非递归实现

 public  static void stackQuickSort(int[] arr,int startIndex,int endIndex){
        //声明一个栈,用来存放每一个数组的起始值和结尾值
        Stack<Map<String,Integer>> mapStack=new Stack<>();
        //第一轮数组的起始,终止下标,以hash的形式入栈
        Map<String,Integer> map=new HashMap<>();
        map.put("startIndex",startIndex);
        map.put("endIndex",endIndex);
        mapStack.push(map);
        //当栈中有元素的时候
        while(!mapStack.isEmpty()){
            //每一次都是取最后一次入栈的元素,是基准元素的右半部分的数组
            Map<String,Integer> popParam=mapStack.pop();
            //获取每一轮排好序之后基准元素的位置
            int pivotIndex=partition(arr,popParam.get("startIndex"),popParam.get("endIndex"));
            //当基准元素左边有元素的时候放在栈里面
            if (popParam.get("startIndex")<pivotIndex) {
                Map<String,Integer> leftParamMap=new HashMap<>();
                leftParamMap.put("startIndex",popParam.get("startIndex"));
                leftParamMap.put("endIndex",pivotIndex-1);
                // 把基准元素左半部分的起止下标放在栈中
                mapStack.push(leftParamMap);

            }
            //当基准元素右边有元素的时候就放在栈里面
            if (popParam.get("endIndex")>pivotIndex) {
                Map<String,Integer> rightParamMap=new HashMap<>();
                rightParamMap.put("startIndex",pivotIndex+1);
                rightParamMap.put("endIndex",popParam.get("endIndex"));
                //把基准元素右半部分的起止下标放在栈中
                mapStack.push(rightParamMap);

            }
        }


    }
    private  static  int partition(int [] arr,int startIndex,int endIndex){
        int pivot=arr[startIndex];
        int mark=startIndex;

        for (int i=startIndex;i<=endIndex;i++){
            if (arr[i]<pivot){
                mark++;
                int p=arr[mark];
                arr[mark]=arr[i];
                arr[i]=p;
            }
        }
        arr[startIndex]=arr[mark];
        arr[mark]=pivot;
        return mark;

    }

    public static void main(String[] args) {
        int arr[]={4,4,6,5,3,2,8,1};
        stackQuickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值