排序算法学习06_快速排序(Java)

325 阅读5分钟

快速排序


前言:在博客写这些文章的目的用于记录所学,怕以后忘了,如果哪里写的不对欢迎指正,谢谢!!

学习目标:掌握快速排序算法的原理和思想

一、前提知识

  排序算法概念、时间复杂度。可前往此网址 排序算法学习01_算法基础介绍阅读

二、快速排序介绍

  快速排序(英语:Quicksort),属于交换排序的一种。又称分区交换排序(partition-exchange sort),我们一般简称为快排

三、快速排序工作原理

  首先了解一下分治法:分治法将问题分解为规模更小的子问题,然后将这些子问题逐个击破,再将已解决的子问题合并,得出母问题的解

  快速排序使用分治法策略,首先它要排序多次,但每次排序时并不真正的对每个数进行排序,而是从序列里找一个基准值序列分成两个部分(子序列),此时这个基准值需所处在序列“中间”一边序列放置比基准值小的元素,另一边序列则要比基准值大。

  然后把比基准值小\大的部分的序列里再找一个基准值又分成两个部分,重复此逻辑。最终将得到一个排序好的结果

四、快速排序算法设计思路

设计一个快速排序算法,需要思考以下这几点:

  1. 基准值选择移动(前面说过,分成俩部分后,比基准值小的在一边,大的在一边)
  2. 如何让比基准值小的部分大的部分基准值两边分开放置
  • 接下来就是重点了,当采用以下方案时,基准值的选择有两种:第一种:序列最左边元素作为基准值。第二种序列最右边作为基准值

    • (还有一种方案是基准值直接选择中间位置的,那就是另一种思路了,这里就不讲了)
  • 首先,我们就要再引入两个变量:

    • 一个为startIndex,该变量用于从序列最左边往右扫描。当要做升序时,扫描时遇到比基准值大,停止
    • 一个为endIndex,该变量用于从序列最右边往左扫描。扫描时遇到比基准值小,停止
    • 两次扫描停止时,交换元素
    • 两个指针相遇时,该位置索引上的值与基准值互换
  • 指针相遇后,则代表某个子序列排序完毕

    • 我们此时要做的操作是:从基准值另一边的子序列进行排序,基准值重新选取。
  • 当子序列元素为1时

    • 当进入排序时,发现startIndex和endIndex重合就能结束了,也就是只有一个元素,就可结束了,去判断其他序列
  • 当所有子序列都排序完毕,最终结果也就出来了

  1. 从上面思路我们也可以了解到,排序时,都是从大的序列排序,再慢慢到小的序列排序,左边序列排序完毕,排右边,这种分治思想,使用递归可以完美解决

五、过程详解

现在选择第一种,选取序列最左边元素作为基准值,此时进行图解如下

  • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610972953056.png" alt="1610972953056" style="zoom:60%;" />

  • 开始扫描,当选取最左边元素作为基准值时,那么需从最右边开始扫描取右则相反

    • endIndex开始扫:扫到0,比3小,直接停止

    • startIndex开始扫:扫2不是,扫到11比3大,停止

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610974693413.png" alt="1610974693413" style="zoom:67%;" />

    • 两者扫描停止,互换元素,如下图:

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610973710139.png" alt="1610973710139" style="zoom:60%;" />

  • 指针还没相遇,则继续扫,依然是endIndex先开始扫

    • 第二次扫

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610975227056.png" alt="1610975227056" style="zoom: 10%;" />

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610975266392.png" alt="1610975266392" style="zoom:40%;" />

    • 第三次扫

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610975422257.png" alt="1610975422257" style="zoom:40%;" />

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610975464921.png" alt="1610975464921" style="zoom:40%;" />

    • 第四次,指针相遇,与基准值互换

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610976360527.png" alt="1610976360527" style="zoom:40%;" />

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610976421794.png" alt="1610976421794" style="zoom:40%;" />

  • 至此,我们第一轮排序结束,基准值左边的都是小的,右边都是大的

  • 接下来则排序基准值左边的子序列,你也可以先判断右边的

    • <img src="C:\Users\66432\AppData\Roaming\Typora\typora-user-images\1610977106994.png" alt="1610977106994" style="zoom:50%;" />

    • 执行逻辑都是一样的

  • 直到什么时候结束捏

    • 当某个子序列结束后,在这里插入图片描述 想再往某个基准值的一边排序时在这里插入图片描述 传值过来发现startIndex和endIndex重合,则表明这个子序列排序完毕

六、代码实现

对该数列进行递增排序 {3,2,11,5,1,7,9,4,-1,-8,0}

package com.migu.sortingAlgorithm;

import java.util.Arrays;

public class QuickSort {
    public static void main(String[] args) {
        int array[] = {3,2,11,5,1,7,9,4,-1,-8,0};
        
        //从两端开始往里找
        quickSort(array,0,array.length-1);
    }

    //采用分治思想
    public static void quickSort(int [] array,int startIndex,int endIndex){
        if(startIndex >= endIndex) return; // 当传入左右索引相遇时,则表明某个序列以排序完毕
        int pivotIndex = sort(array, startIndex, endIndex);  // sort方法,用于实现排序.并返回一个基准值,用于判断其他范围的序列
        quickSort(array,startIndex,pivotIndex-1);   // 这是基准值左边的序列
        quickSort(array,pivotIndex+1,endIndex);    // 这是右边的序列
    }
    //排序实现
    private static int sort(int[] array,int startIndex,int endIndex){
        // 定义四个变量: left:某个序列最左边索引,right:某个序列最右边索引。作用就是找大小
        //             pivot:基准值  temp:辅助交换
        int left,right,pivot,temp;

        pivot = array[startIndex]; // 该算法需要选某个序列最左/右边的元素为基准值
        left = startIndex; // 从某个序列最左边,往右走
        right = endIndex;  // 从某个序列最右边,往左走
        while (true){
		   // 基准值在左边,那么从右边开始找,当left和right重合时停止扫描	
            while (array[right] >= pivot && left < right) right--; // 找到小于基准值退出
            while (array[left] <= pivot && left < right) left++; // 找到大于基准值退出

            //得到一大一小进行交换
            temp = array[left];
            array[left] = array[right];
            array[right] = temp;
            
            if(left >= right) break; // 指针相遇退出循环
        }
        // 指针相遇,相遇位置的元素和基准值交换
        temp = array[startIndex];
        array[startIndex] = array[left];
        array[left] = temp;
        // 返回基准值此时所在的索引,以便排序其他子序列
        return left;
    }
}

七、时间复杂度

  在平均状态下:该算法排序n个项目复杂度为O(n log n),极端情况下可能会想冒泡排序一样为O(n^2),一般不可能发生

八、总结

  快速排序算法,采用分治法,实现跳跃式的'冒泡排序来对数据进行排序。

  理清基准值两边元素是如何放置的,即可轻松掌握快速排序