图解快速排序算法

ced485cbb11e458d81a746890b32cf3f.gif

作者:敲代码の流川枫

博客主页:流川枫的博客

专栏:和我一起学java

语录:Stay hungry stay foolish

工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器——牛客网

点击免费注册和我一起刷题吧    

文章目录

1. 算法思想

2. 算法图解

3. 代码实现

4. 快排特点及性能


1. 算法思想

快速排序的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据比另一部分的所有数据要小,再按这种方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,使整个数据变成有序序列

步骤:

1. 选定Pivot为中心轴

2. 将大于Pivot的数字放在Pivot的右边

3. 将小于Pivot的数字放在Pivot的左边

4. 分别对左右子序列重复前三步操作

2. 算法图解

int[] array = {25,100,9,20,1,8};

以数组array为例图解快速排序

1. 为了方便起见,我们每次都选取最左边的数字为中心轴Pivot,要把小于19的数字放在19左边,把大于19的数字放在19右边,如何达到目的呢?我们设置了L和R两个下标如图

2. L从左向右移动,一旦发现比Pivot大的数字,将它放在该序列右边;R从右向左移动,一旦发现有数字小于Pivot,则把数字放到该序列左边,最终LR会在某个位置相遇重合,将Pivot放在这个位置,至此第一次排序结束。此时左边的数全小于Pivot,右边的数全大于Pivot,形成左右子序列 

R指向8,8<25,将它放到序列左边L处

L和R交替移动,L指向100,100>25,将它移动到序列右边R处

 R和L交替移动,R向左移动,指向1,1<25,将它移动到序列左边L处

L和R交替移动,L指向9,9<25,将它放在原位,L继续移动

L向左移动指向20,20<25,将他放在原位,L继续左移

 L和R相遇,将Povit放在相遇的位置,并且分出来了左右子序列如图:

3. 现在对左子序列和右子序列进行同样的操作,因为右子序列只有一个数,默认有序

4. 对右子序列继续排序:

5.  至此排序完成:

3. 代码实现

代码:

public class QuickSort {
    private int[] array;
    public QuickSort(int[] array){
        this.array = array;
    }
    public void sort() {
        quickSort(array,0,array.length-1);
    }
    public void print() {
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }

    public void quickSort(int[] array,int left,int right) {
        if(left<right){
            int Povit = array[left];
            int L = left;
            int R = right;
            while(L<R){
                while(L < R && array[R]>Povit){
                    R--;
                }
                if(L < R) {
                    array[L] = array[R];
                    L++;
                }
                while(L < R && array[L] < Povit){
                    L++;
                }
                if(L < R){
                    array[R] = array[L];
                    R--;
                }
            }
            array[R] = Povit;
            quickSort(array,left,R-1);
            quickSort(array,R+1,right);
        }
    }
}

class SortTest {
    public static void main(String[] args) {
        testQuickSort();
    }
    /**
     * 快速排序
     */
    private static void testQuickSort() {
        int[] array = {25,100,9,20,1,8};
        QuickSort quickSort = new QuickSort(array);
        quickSort.sort();
        quickSort.print();
    }
}

结果: 

4. 快排特点及性能

特点:

快速排序是在冒泡排序的基础上改进而来的,冒泡排序每次只能交换相邻的两个元素,而快速排序是跳跃式的交换

快速排序是一个不稳定的算法,在经过排序之后,可能会对相同值的元素的相对位置造成改变

快速排序基本上被认为是相同数量级的所有排序算法中,平均性能最好的

性能:

时间复杂度

快速排序在最坏情况下的时间复杂度和冒泡排序一样,是 O(n2),实际上每次比较都需要交换,但是这种情况并不常见,思考一下如果每次比较都需要交换,那么数列的平均时间复杂度是 O(nlogn),事实上在大多数时候,排序的速度要快于这个平均时间复杂度。这种算法实际上是一种分治法思想,也就是分而治之,把问题分为一个个的小部分来分别解决,再把结果组合起来

空间复杂度

快速排序只是使用数组原本的空间进行排序,所以所占用的空间应该是常量级的,但是由于每次划分之后是递归调用,所以递归调用在运行的过程中会消耗一定的空间,在一般情况下的空间复杂度为 O(logn),在最差的情况下,若每次只完成了一个元素,那么空间复杂度为 O(n)。所以我们一般认为快速排序的空间复杂度为 O(logn)

“ 本期的分享就到这里了, 记得给博主一个三连哈,你的支持是我创作的最大动力!

ced485cbb11e458d81a746890b32cf3f.gif

<think>我们正在处理用户关于C++快速排序算法图解说明请求。根据提供的引用资料,我们有以下信息:引用[1]提到了图解快速排序算法和快速选择算法,并详细描述了partition函数的使用。引用[2]介绍了快速排序是一种分治算法,并给出了分治算法的解题方法。引用[3]提供了一个C++快速排序的代码实现,并附有注释。用户需要的是图解教程,因此我们的回答应该以图文结合的方式解释快速排序的过程,但由于当前是文本形式,我们将用文字详细描述每一步的过程,并尽可能用比喻和步骤分解来模拟图解效果。快速排序的基本思想:1.选择基准元素(pivot)。2.分区(partition):将数组分为两部分,使得左边部分的所有元素小于等于基准,右边部分的所有元素大于等于基准。3.递归地对左右两部分进行快速排序。我们将使用引用[3]中的代码作为示例,并逐步解释其过程。步骤分解:1.初始化:选择数组第一个元素作为基准(pivot),设置左右指针(left和right)分别指向数组的起始和末尾。2.分区过程:a.从右向左移动right指针,直到找到小于基准的元素。b.将该元素放到left指针位置(此时该元素已经移到左边,原位置可视为空)。c.从左向右移动left指针,直到找到大于基准的元素。d.将该元素放到right指针位置(此时原left位置的元素移到右边,left指针位置可视为空)。e.重复上述步骤,直到left和right指针相遇,此时将基准元素放入该位置。3.递归排序:对基准左右两侧的子数组重复上述过程。为了更形象地解释,我们可以用一个具体数组作为例子,逐步展示每一步的变化。例子:对数组[5,6,3,2,7,8,9,1,4]进行快速排序(注意引用[3]的代码中数组有两个0,这里去掉以简化)。由于用户要求图解,我们无法直接画图,但可以用文本模拟每一步的数组状态。下面结合引用[3]的代码进行说明:```cppvoidquickSort(intarr[],intbegin,intend){if(begin>=end)return;intleft=begin;intright=end;inttemp=arr[left];//基准选择第一个元素while(left<right){//从右向左找第一个小于temp的元素while(left<right&&arr[right]>=temp){right--;}if(left<right){arr[left]=arr[right];//将找到的小于temp的元素放到left位置}//从左向右找第一个大于temp的元素while(left<right&&arr[left]<=temp){left++;}if(left<right){arr[right]=arr[left];//将找到的大于temp的元素放到right位置}//当left和right相遇时,将基准temp放到该位置if(left==right){arr[left]=temp;}}//递归排序左半部分和右半部分quickSort(arr,begin,left-1);quickSort(arr,left+1,end);}```现在,我们以数组[5,6,3,2,7,8,9,1,4]为例,详细展示第一次分区过程:初始状态:arr=[5,6,3,2,7,8,9,1,4]left=0,right=8,temp=5步骤1:从右向左找小于5的元素。right从8开始,arr[8]=4<5,所以找到,此时将arr[left](即arr[0])赋值为4,数组变为[4,6,3,2,7,8,9,1,4](注意:此时原arr[0]的值5被覆盖,但保存在temp中,而arr[8]的值4被复制到了arr[0],所以arr[8]的位置现在可以认为空出来了,但实际上还没有改变,因为紧接着我们要从左向右找大于5的元素)。然后,left向右移动(left++),直到找到大于5的元素。left=1时,arr[1]=6>5,所以找到。将arr[right](即arr[8])赋值为6,数组变为[4,6,3,2,7,8,9,1,6](此时arr[1]的值6被复制到了arr[8],arr[1]位置空出?这里注意,实际上在赋值后,arr[1]的值并没有被覆盖掉,因为我们将它赋值给了arr[8])。然后,right指针需要继续向左移动(因为left<right,所以继续循环)。当前left=1,right=8。步骤2:再次从右向左找小于5的元素。right从8开始向左移动,此时arr[8]=6>5,所以继续移动。right=7,arr[7]=1<5,找到。将arr[left](即arr[1])赋值为1,数组变为[4,1,3,2,7,8,9,1,6](这里将arr[1]的值6覆盖成了1,而arr[7]的值1被复制到了arr[1])。然后,left向右移动(left++),从1开始,left=1时arr[1]=1<=5,继续;left=2时3<=5,继续;left=3时2<=5,继续;left=4时7>5,找到。将arr[right](即arr[7])赋值为7,数组变为[4,1,3,2,7,8,9,7,6](将arr[4]的值7复制到了arr[7])。然后,right指针继续向左移动(left=4,right=7),现在从right=7开始向左,arr[7]=7>5,继续;right=6时9>5,继续;right=5时8>5,继续;right=4时,此时left==right,循环终止。然后,将基准值5放入arr[left](即arr[4]),数组变为[4,1,3,2,5,8,9,7,6]。这样,第一次分区完成,基准值5已经位于正确的位置(下标4),其左边的元素[4,1,3,2]都小于5,右边的元素[8,9,7,6]都大于5。接下来,递归排序左子数组[4,1,3,2]和右子数组[8,9,7,6]。注意:在代码中,我们递归调用:quickSort(arr,begin,left-1);//排序左子数组,即下标0到3quickSort(arr,left+1,end);//排序右子数组,即下标5到8这样逐步递归,直到整个数组有序。为了更清晰,我们可以用树状结构表示递归过程,但限于文本,我们只描述第一层分区,然后对左右子数组的分区过程类似进行。总结图解要点:1.每次分区,基准元素归位。2.分区过程通过左右指针交替扫描,将小于基准的元素移到左边,大于基准的元素移到右边。3.递归分区直到每个子数组只有一个元素或为空。此外,引用[1]还提到了快速选择算法,它是快速排序的变种,用于快速找到第k大的元素,但用户主要关注快速排序,所以我们不展开。最后,根据指令,我们需要生成相关问题。</think>### C++ 快速排序算法图解教程 快速排序是一种高效的分治排序算法,其核心思想是通过**基准元素(pivot)** 将数组划分为左右两个子数组,再递归排序子数组。以下是详细步骤和代码实现: --- #### **算法步骤图解(以数组 `[5, 3, 1, 6, 4, 2]` 为例)** 1. **选择基准元素** - 通常选第一个元素为基准(`pivot=5`)。 - 设置左右指针:`left` 指向起始位置,`right` 指向末尾。 ![初始状态](https://img-blog.csdnimg.cn/direct/1e5e4d3e4d3b4c7a9b0e8c3e3d3e3e3e.png) 2. **分区操作(Partition)** - **从右向左扫描**:`right` 左移,找到第一个 `< pivot` 的元素(`2<5`),将其复制到 `left` 位置。 ```bash 原数组: [5, 3, 1, 6, 4, 2] 操作后: [2, 3, 1, 6, 4, 2] # 将2移到left位置,此时right位置空出 ``` - **从左向右扫描**:`left` 右移,找到第一个 `> pivot` 的元素(`6>5`),将其复制到 `right` 位置。 ```bash 操作后: [2, 3, 1, 6, 4, 6] # 将6移到right位置,此时left位置空出 ``` - **重复交替扫描**,直到 `left == right`,将 `pivot` 放入该位置: ```bash 最终分区: [2, 3, 1, 4, 5, 6] # 5已归位,左侧元素均<5,右侧均>5 ``` 3. **递归排序子数组** - 对左子数组 `[2, 3, 1, 4]` 和右子数组 `[6]` 重复上述过程。 - 递归终止条件:子数组长度 `≤1`(已有序)。 --- #### **C++ 代码实现** ```cpp #include <iostream> using namespace std; void quickSort(int arr[], int begin, int end) { if (begin >= end) return; // 递归终止 int left = begin, right = end; int pivot = arr[left]; // 选择第一个元素为基准 while (left < right) { // 从右向左找小于pivot的元素 while (left < right && arr[right] >= pivot) right--; if (left < right) arr[left] = arr[right]; // 从左向右找大于pivot的元素 while (left < right && arr[left] <= pivot) left++; if (left < right) arr[right] = arr[left]; // 当左右指针相遇,放入基准值 if (left == right) arr[left] = pivot; } // 递归排序左右子数组 quickSort(arr, begin, left - 1); quickSort(arr, left + 1, end); } int main() { int arr[] = {5, 3, 1, 6, 4, 2}; int n = sizeof(arr) / sizeof(arr[0]); quickSort(arr, 0, n - 1); // 输出排序结果: 1 2 3 4 5 6 for (int i = 0; i < n; i++) cout << arr[i] << " "; return 0; } ``` --- #### **关键点解析** 1. **分区过程**: - 通过双指针交替扫描,将小于基准的元素移到左侧,大于基准的元素移到右侧[^1]。 - 每次分区确保基准元素归位到最终位置。 2. **时间复杂度**: - 平均:$O(n \log n)$(每次分区均匀划分)。 - 最坏:$O(n^2)$(数组已有序时,退化为冒泡排序)。 3. **优化策略**: - 随机选择基准(避免最坏情况)。 - 三数取中法(选头、中、尾的中位数作为基准)[^2]。 ---
评论 93
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YoLo♪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值