JAVASE 小白学习笔记 (13-4)Java中数组排序算法-快速排序

1.快速排序的引入

快速排序是从冒泡牌序演变而来的算法,但是要比冒泡排序高效很多,所以叫做快速排序。

快速排序
排序之所以快速,是因为此算法采用分治法。同冒泡排序一样,快速排序也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的。
不同的是,冒泡排序在每一轮只将一个元素冒泡到数组的一端,而快速排序在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列的一端,比它小的元素移动到数组的另一端,从而将数组拆分为两部分。

2.快速排序的排序思想

2.1 快速排序的核心思想:分治法

在这里插入图片描述

每一次数组分为两部分,到底有什么好处?
给定假设8个元素的数组,一般情况下冒泡排序需要比较8轮(实际上比较7轮数组已经为有序数组),每一轮将一个元素移动到数组的一端。

快速排序的流程是:

在这里插入图片描述

如图在分治法的思想下,原数组在每一轮被拆分为了两部分,每一部分的下一轮又分别被拆分名为了两部分,直到不可再分为止。

这样一共需要多少轮呢?平均情况下需要logn轮,因此快速排序算法的时间复杂度为O(nlogn)。


2.2 快速排序的核心:基准元素的选择与移动

分治法极大的缩减了排序过程中比较的次数,但基准元素的选择与移动都是快速排序的核心问题。

2.2.1 基准元素的选择

基准元素,英文pivot,用于在分治过程中以此为中心,把其他元素移动到基准元素的左右两边。

那么基准元素该如何选择呢?

最简单的方式是选择数组的第一个元素:

在这里插入图片描述

这种选择的方式大部分情况下是没有问题的,但当给定的数组是一个逆序的数组,想要把这个数组转换为正序的数组,就会发生下面的情况。

在这里插入图片描述

观察上图我们可以看出整个数组并没有被分成一半一半,每一轮仅仅是确定了基准元素的位置。这种情况下,数组的第一个元素不是最小值就是最大值,无法发挥出分治法的优势。在这种极端的情况下,快速排序要经过N轮排序,时间复杂度变为了O(N^2)。

但通常上我们可以尽量避免此种情况的发生,采取的方式是我们可以不选择数组的第一个元素,而是随机选择一个元素作为基准元素。

在这里插入图片描述
这样即使数组处于完全逆序的情况下,也可以有效地将数组分成两部分。
当然即使是随机选择基准元素,也有很小的概率选到最小值或者最大值作为基准元素,会影响到分治的影响。

所以快速排序的平均时间复杂度为O(nlogn),最坏情况下(选择最小值或者最大值作为基准元素)时间复杂度为O(n^2)。

2.2.2 元素的移动(挖坑法)

快速排序的算法思想
快速排序的算法思想是分治法,比大小,再进行分区。1.首先从数组中取出第一个数作为基准数 2.将这个数大于或者等于的数全放在它的右边,小于它的数放在它的左边。 3.再对左右区间重复操作第二步,直到各区间只有一个数

这里元素的移动采用的是挖坑法,这里需要将挖坑法的具体实现思路进行说明。

挖坑填数的实现思路
1.将基准数挖出形成第一个坑
2.由后往前找比它小的数,找到后挖出此数,填到前一个坑中
3.由前往后找比它大于或等于的数,找到后,也挖出此数,填到前一个坑中
4.在重复进行执行第二步与第三步的操作

3.快速排序的代码实现

对于数组{5,3,9,1,6,7,2,4,0,8}进行快速排排序

挖坑填数第一轮:
在这里插入图片描述
如此进行重复挖坑填数的第2与第3步操作,即可完成最终的顺序

public class QuickSortUtils {
    
    
    //快速排序
    public static void quickSort(int []arr,int start,int end){
    
    
        /*挖坑填数:得到基准数所在索引位置,以这个位置分成左右两个区。
        然后对左右两区进行递归调用。
         */
        if(start<end){
    
    
            //获取分成左右两区基准数所在的索引位置
           int index= getIndex(arr,start,end);
           //对左区进行递归
            quickSort(arr,start,index-1);
            //对右区进行递归
            quickSort(arr,index+1,end);
        }
    }

    /*
     挖坑填数:1.将基准数挖出来形成第一个坑
              2.由后往前找到比它小的数,找到后挖出此数填到前一个坑
              3.由前往后找到比它大于等于的数,找到后也挖出此数填到前一个坑
              4.再重新执行2.3操作步骤
     */
    private static int getIndex(int[] arr, int start, int end) {
    
    
        int i=start;
        int j=end;
        //定义基准数
        int x=arr[i];
        //重复2,3步骤
        while(i<j){
    
    
            //2.由后往前找比它小的数,找到后挖出此数填到前一个坑中
            while(i<j&&arr[j]>=x){
    
    
                j--;
            }
            //找到后挖出此数,填到前一个坑中
            if(i<j){
    
    
                arr[i]=arr[j];
                i++;   //找到后让i顺便递增下
            }
            //3.由前往后找到比它大或等于的数,找到后也挖出此数,填到前一个坑中
            while(i<j&&arr[i]<x){
    
    
                i++;
            }
            //找到后也挖出此数填到前一个坑中
            if(i<j){
    
    
                arr[j]=arr[i];
                j--;//顺便让j再减一下
            }
        }
        //把基准数填到最后一个坑中
        arr[i]=x;
        return i;//返回基准数所在的索引位置
    }
}

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

    }
}

运行结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41537102/article/details/112604611
今日推荐