我们可以通过一个具体案例来解释int[] nums, int lo, int hi 的变化

让我们用一个具体的例子来详细解释 int[] nums, int lo, 和 int hi 在快速排序的 partition 函数中的变化,以及这个过程如何对数组进行排序。

示例数组

我们将使用下面的数组进行演示:

int[] nums = {3, 6, 8, 10, 1, 2, 1};

初始调用

首先,我们调用 sort 方法来对整个数组进行排序:

sort(nums, 0, nums.length - 1);
  • 初始参数:
    • nums = {3, 6, 8, 10, 1, 2, 1}
    • lo = 0
    • hi = 6 (即数组的最后一个元素索引)

Partition 函数执行

  1. 选择基准点:

    • pivot = nums[hi] = nums[6] = 1
  2. 初始化:

    • i = lo - 1 = 0 - 1 = -1
  3. 遍历数组并分区:

    • 遍历从 j = loj = hi - 1,逐个比较元素与基准点 pivot
遍历和比较过程:
Stepjnums[j]iArray StatusSwap?
103-1{3, 6, 8, 10, 1, 2, 1}No
216-1{3, 6, 8, 10, 1, 2, 1}No
328-1{3, 6, 8, 10, 1, 2, 1}No
4310-1{3, 6, 8, 10, 1, 2, 1}No
541-1{3, 6, 8, 10, 1, 2, 1}No
652-1{3, 6, 8, 10, 1, 2, 1}No
7610{3, 6, 8, 10, 1, 2, 1} - Swap(6,6)Yes - Swap
  • 步骤 1 到 5:

    • nums[j] > pivot for all elements except nums[4] (which equals pivot). Therefore, i remains unchanged at -1, and no swaps occur.
  • 步骤 6:

    • nums[j] = 2 > pivot, so no swap occurs. i remains -1.
  1. 放置基准点:

    • 最后,将基准点放置在正确位置:
    • swap(nums, i + 1, hi):
      • swap(nums, 0, 6):
      • 这将把基准点 1 交换到索引 0 处。
  2. 结果:

    • Array status: {1, 6, 8, 10, 3, 2, 1}
    • 返回分界点索引 p = i + 1 = 0
结果分析

基于上述步骤,partition 函数已经将数组分成两部分:

  • 左边部分包含小于或等于 pivot 的元素。
  • 右边部分包含大于 pivot 的元素。

此时,数组已经部分排序。继续递归调用 sort 方法进行剩余排序。

递归调用

  1. 左侧子数组:

    sort(nums, lo, p - 1);
    sort(nums, 0, -1); // 递归停止
    
    • 因为 lo > hi,所以递归停止。
  2. 右侧子数组:

    sort(nums, p + 1, hi);
    sort(nums, 1, 6);
    

第二次 Partition 调用(右侧子数组)

接下来,我们对右侧子数组 {6, 8, 10, 3, 2, 1} 继续进行 partition 操作:

  • 选择基准点:

    • pivot = nums[6] = 1
  • 初始化:

    • i = 1 - 1 = 0
  • 遍历和比较过程:

Stepjnums[j]iArray StatusSwap?
1160{1, 6, 8, 10, 3, 2, 1}No
2280{1, 6, 8, 10, 3, 2, 1}No
33100{1, 6, 8, 10, 3, 2, 1}No
4430{1, 6, 8, 10, 3, 2, 1}No
5520{1, 6, 8, 10, 3, 2, 1}No
6611{1, 1, 8, 10, 3, 2, 6} - Swap(6,1)Yes - Swap
  • 放置基准点:

    • swap(nums, i + 1, hi):
    • swap(nums, 1, 6):
    • 基准点 1 被交换到索引 1 处。
  • 结果:

    • Array status: {1, 1, 8, 10, 3, 2, 6}
    • 返回分界点索引 p = i + 1 = 1

递归继续

继续对右侧子数组 {8, 10, 3, 2, 6} 进行快速排序。

完整递归过程分析

为了更好地理解整个过程,我们可以从开始到结束逐步分析递归调用和分区情况。

初始数组状态

nums = {3, 6, 8, 10, 1, 2, 1}

  1. 第一次 partition:

    • pivot = 1
    • Array after partition: {1, 6, 8, 10, 3, 2, 1}
    • p = 0
  2. 递归调用:

    • 左侧: sort(nums, 0, -1) - 停止
    • 右侧: sort(nums, 1, 6)
  3. 第二次 partition:

    • 子数组: {6, 8, 10, 3, 2, 1}
    • pivot = 1
    • Array after partition: {1, 1, 8, 10, 3, 2, 6}
    • p = 1
  4. 递归调用:

    • 左侧: sort(nums, 1, 0) - 停止
    • 右侧: sort(nums, 2, 6)
  5. 第三次 partition:

    • 子数组: {8, 10, 3, 2, 6}
    • pivot = 6
    • Array after partition: {1, 1, 2, 3, 6, 8, 10}
    • p = 4
  6. 递归调用:

    • 左侧: sort(nums, 2, 3)
    • 右侧: sort(nums, 5, 6)
  7. 第四次 partition:

    • 子数组: {2, 3}
    • pivot = 3
    • Array after partition: {1, 1, 2, 3, 6, 8, 10}
    • p = 3
  8. 递归调用:

    • 左侧: sort(nums, 2, 2) - 停止
    • 右侧: sort(nums, 4, 3) - 停止
  9. 第五次 partition:

    • 子数组: {8, 10}
    • pivot = 10
    • Array after partition: {1, 1, 2, 3, 6, 8, 10}
    • p = 6
  10. 递归调用:

    • 左侧: sort(nums, 5, 5) - 停止
    • 右侧: sort(nums, 7, 6) - 停止

最终排序结果

经过以上的步骤,最终排序完成的数组如下:

int[] nums = {1, 1, 2, 3, 6, 8, 10};

递归调用树

以下是快速排序过程中的递归调用树,用于更好地理解每一步的递归操作:

sort(nums, 0, 6)
    ├── sort(nums, 0, -1)
    └── sort(nums, 1, 6)
        ├── sort(nums, 1, 0)
        └── sort(nums, 2, 6)
            ├── sort(nums, 2, 3)
            │   ├── sort(nums, 2, 2)
            │   └── sort(nums, 4, 3)
            └── sort(nums, 5, 6)
                ├── sort(nums, 5, 5)
                └── sort(nums, 7, 6)

总结

  • 分区: 通过 partition 函数对数组进行分区,确保基准点左侧元素小于基准点,右侧元素大于基准点。
  • 递归: 递归调用快速排序对每个分区的子数组进行排序,直至数组完全有序。

这个示例演示了快速排序的完整过程,从选择基准点到分区,再到递归调用排序,最终达到数组排序的效果。

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值