快速排序|三分钟包教包会|思路详解

概述

        快速排序(Quick Sort)是一种高效的排序算法,由英国计算机科学家C. A. R. Hoare在1962年提出。它的基本思想是分治法(分而治之)

思路

        之前学快排的时候看过很多大佬的思路,但是都是在讲快排是怎么实现的,对于感觉对于很多刚接触这个排序算法的朋友来说有点不友好。直到有一天,学到了看到一个大佬在讲分治的时候,举了一个例子,感觉非常好,很适合对于初学快排的朋友更好理解它的思路:

例子:现在你手上有100份试卷,我们需要把他们按照分数从低到高进行排序,我们该如何排序?

        如果没有学过算法的朋友可能是这样排的:我们先拿出第一张试卷,看然后把它放在桌子上,接着拿第二张试卷,看当前这试卷的分值比之前的试卷的分值大还是小,如果比它分值大就把它放在第一张试卷下面,如果比它分值小,我们就把它放在第一张试卷上面,以此类推,我们就可以按照这样的方式把它全部排好。

        这是传统的排序方式,我们一般称它为插入排序,对于一个班只有几十个人的情况,完全够用了。但是如果我们需要排序的试卷很多呢?有500张,1000张?显然这样的方式就显得有点力不从心了,这时我们就可以用到今天我们所学的快排了。

我们可以这样做:

  1. 我们有100份试卷,我们按照第一张试卷(最上面的一张)上面的分数把试卷分成两堆,分法为:把分数小于等于第一张试卷的试卷放到左边,把分数大于第一张试卷的试卷放到右边(这个操作听起来麻烦,不过很快就能分完,因为我们并不需要排序,看一眼分数直接分就行),这样我们就得到了两堆试卷,且右边的试卷分数一定比左边的试卷分数高。
  2. 接着我们最左边的这一堆的第一张试卷,和这一堆最后一张试卷的调换一下位置,因为这张试卷分数一定是最左边这一堆里面分数最高的(且调换过后方便分堆,因为如果一直按照这个试卷来分的话,下一次分堆时左边这一堆则一定不能分成两堆了)
  3. 我们接着按照第一步的方式,我们把左边这一堆的试卷按照第一张试卷(当前这一堆试卷最上面的一张)上面的分数把试卷又分成两堆,然后把分数小于等于第一张试卷的试卷放到左边,把分数大于第一张试卷的试卷放到右边
  4. 同样的方式,我们可以把右边这一堆试卷也能分成两份。于是我们得到了四堆试卷,且排在右边的一定比排在左边的分数高(可能没有四堆因为第一张试卷分数可能就是最大的或者最小的,但是不影响操作)
  5. 按照之前的方式我们就可以把两堆变四堆四堆变八堆。。。直到分到每堆只有一张试卷的时候就结束。(其实现实情况下是,当分出8堆过后每一堆也就没多少了,然后找几个学生娃儿自己去排序就好了,哈哈哈哈)
  6. 当我们全部把这些试卷铺开后那么我们也就已经给这些试卷排好了序。(手动狗头)

通过这个例子我们就能大致理解快排的思想了,那么在程序里面我们怎么实现呢?

按照刚才的例子具体步骤如下:

  • 选择基准值(第一张试卷上的分数)
  • 初始化两个指针,left指向序列的开始,right指向序列的末尾。(用于遍历数组)
  • 移动left指针直到找到一个大于基准值的元素,同时移动right指针直到找到一个小于基准值的元素。(从左到右找到大于基准数的数,在从右到左找到小于基准数的数)
  • 如果left小于right,交换left和right指针指向的元素。(分堆,让右边的数一定大于左边的数)
  • 重复步骤3和4,直到left与right相遇或交错,这时将基准值交换到相遇位置。(把第一张试卷和最后一张试卷位置对调)
  • 递归地对基准值左侧和右侧的子序列进行快速排序。(使用递归的方式重复分堆操作)

代码(附注释):

void QuickSort(int array[], int left, int right) 
{
	int i = left;
	int j = right;
    if(i >= j) { //当试卷分到只剩下一张时则没有分的必要了 
        return ;
    }
    int temp = array[left];//选取第一张试卷的分数作为基准值 
    while(i != j) {
        while(array[j] >= temp && i < j) {//先从左到右遍历第一个小于基准值的数 
            j--;
        }
		while(array[i] <= temp && i < j) {//再从右到左找第一个大于基准值的数 
            i++; 
        }
		if(i < j) {//确定好找到的两个数后进行交换保证左边一定比右边大 
            swap(array[i], array[j]);
        }
    }

    swap(array[left], array[i]);//交换第一张和最后一张试卷的位置 
    QuickSort(array, left, i - 1);//对左边的试卷进行分堆 
    QuickSort(array, i + 1, right);//对右边的试卷进行分堆 
}

代码(无注释):

void QuickSort(int array[], int left, int right) 
{
	int i = left;
	int j = right;
    if(i >= j) {  
        return ;
    }
    int temp = array[left]; 
    while(i != j) {
        while(array[j] >= temp && i < j) {
            j--;
        }
		while(array[i] <= temp && i < j) { 
            i++; 
        }
		if(i < j) { 
            swap(array[i], array[j]);
        }
    }

    swap(array[left], array[i]); 
    QuickSort(array, left, i - 1); 
    QuickSort(array, i + 1, right); 
}

注意:

        使用递归,一定要注意递归结束条件

        一定要先从左边开始遍历,再从右边开始遍历,不然会导致交换基准数出错

总结

        快速排序之所以高效,是因为在平均情况下,每次分区操作都能将数组大致平均分成两部分,这使得算法的递归深度保持在O(log n)级别。在最好情况下,快速排序的时间复杂度为O(n log n);但在最坏情况下,例如当输入的数组已经有序时,时间复杂度会退化到O(n^2)。为了优化这种情况,可以采用随机化快速排序,即在每次分区前随机选择基准值,以减少最坏情况出现的概率。        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值