为什么经典快排先从右往左找

本文详细探讨了经典快速排序算法中,为何选择从右往左寻找基准点的交换条件。通过分析代码和不同选择的后果,揭示了在基准点为arr[lo]时,从右往左找能确保满足arr[x]<arr[lo]的交换条件,从而保证正确排序。同时,也对比了从左往右找的情况,指出其适用的基准点应为arr[hi]。此外,还提及了随机选择基准点的方法,以增加算法的稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【为什么经典快排先从右往左找】

教科书的经典做法,总是选择 lo 作为基准点,lo[lo, hi] 区间的左边,当跳出大的 while i < j 的时候,交换分界点 arr[x]arr[lo] 的值,让基准点落在左右的分界点上,因为 arr[x] 要交换到左边,那就要求 arr[x] < arr[lo],在这种情况下,只能选择,先从右往左找,具体原因,下面分析。

先从右往左找

def qsort(arr, lo, hi):

    if lo >= hi:
        return

    i = lo
    j = hi

    # 选择 lo 为基准点
    pivot = arr[lo]

    while i < j:

        while i < j and arr[j] >= pivot:
            j -= 1
        
        while i < j and arr[i] <= pivot:
            i += 1
        
        arr[i], arr[j] = arr[j], arr[i]
    
    arr[lo], arr[j] = arr[j], arr[lo]

    qsort(arr, lo, i - 1)
    qsort(arr, i + 1, hi)

代码里,L22 就是让基准点 arr[lo] 交换到分界点上。

在这里面,跳出 L12while i < j 可以推出 i = j,即左右指针相遇点即为分界点。

arr[lo], arr[x] = arr[x], arr[lo]

其中的 x 就是 ij 的最终值。

主要分析 arr[x] < arr[lo] ?

L12 要跳出,真实跳出点为 L14L17,因为只有 L14L17 中才有对 ij 的值的更新,下面各自进行讨论。

若在 L14 跳出

    while i < j:

        # 上一次循环的结果
        # arr[i] < pivot, arr[j] > pivot
        while i < j and arr[j] >= pivot:
            j -= 1 # 若 j 进行自减后 等于 i,跳出大循环,根据上一次循环结果,则 arr[j] = arr[i] < pivot
        # 若不跳出大循环,仅上面顺利执行下来,则 arr[j] < pivot
        
        while i < j and arr[i] <= pivot:
            i += 1
        # 若不跳出大循环,仅上面顺利执行下来,则 arr[i] > pivot
        
        # arr[i] > pivot, arr[j] < pivot
        arr[i], arr[j] = arr[j], arr[i]
        # 交换过后,转到下一次循环的结果
        # arr[i] < pivot, arr[j] > pivot

根据上面代码块的 L4L6 可知,arr[j] < pivot 符合 arr[x] < arr[lo] 的条件,可进行交换。

若在 L17 跳出

    while i < j:

        while i < j and arr[j] >= pivot:
            j -= 1
        # 若不跳出大循环,仅上面顺利执行下来,则 arr[j] < pivot
        
        # 若在 L17 跳出,则说明上面两行可顺利执行
        while i < j and arr[i] <= pivot:
            i += 1 # 若 i 进行自增后 等于 j,跳出大循环,根据上面顺利执行的结果,则 arr[i] = arr[j] < pivot
        # 若不跳出大循环,仅上面顺利执行下来,则 arr[i] > pivot
        
        # arr[i] > pivot, arr[j] < pivot
        arr[i], arr[j] = arr[j], arr[i]
        # 交换过后,转到下一次循环的结果
        # arr[i] < pivot, arr[j] > pivot

根据上面代码块的 L5L9 可知,arr[i] < pivot 符合 arr[x] < arr[lo] 的条件,可进行交换。

先从左往右找

def qsort(arr, lo, hi):

    if lo >= hi:
        return

    i = lo
    j = hi

    # 选择 lo 为基准点
    pivot = arr[lo]

    while i < j:

        while i < j and arr[i] <= pivot:
            i += 1

        while i < j and arr[j] >= pivot:
            j -= 1
        
        arr[i], arr[j] = arr[j], arr[i]
    
    arr[lo], arr[j] = arr[j], arr[lo]

    qsort(arr, lo, i - 1)
    qsort(arr, i + 1, hi)

首先说明,在基准点选择 lo 的情况下,先从左往右找,最终结果是不对的。

同样进行分析,主要还是跳出 L12while i < j,真实跳出点为 L14L17,各自进行讨论。

若在 L14 跳出

    while i < j:

        # 上一次循环的结果
        # arr[i] < pivot, arr[j] > pivot
        while i < j and arr[i] <= pivot:
            i += 1 # 若 i 进行自增后 等于 j,跳出大循环,根据上一次循环结果,则 arr[i] = arr[j] > pivot
        # 若不跳出大循环,仅上面顺利执行下来,则 arr[i] > pivot

        while i < j and arr[j] >= pivot:
            j -= 1 
        # 若不跳出大循环,仅上面顺利执行下来,则 arr[j] < pivot
        
        # arr[i] > pivot, arr[j] < pivot
        arr[i], arr[j] = arr[j], arr[i]
        # 交换过后,转到下一次循环的结果
        # arr[i] < pivot, arr[j] > pivot

根据上面代码块的 L4L6 可知,arr[i] > pivot 不符合 arr[x] < arr[lo] 的条件,不可进行交换。

若在 L17 跳出

    while i < j:

        while i < j and arr[i] <= pivot:
            i += 1
        # 若不跳出大循环,仅上面顺利执行下来,则 arr[i] > pivot

        # 若在 L17 跳出,则说明上面两行可顺利执行
        while i < j and arr[j] >= pivot:
            j -= 1 # 若 j 进行自减后 等于 i,跳出大循环,根据上面顺序执行的结果,arr[j] = arr[i] > pivot
        # 若不跳出大循环,仅上面顺利执行下来,则 arr[j] < pivot
        
        # arr[i] > pivot, arr[j] < pivot
        arr[i], arr[j] = arr[j], arr[i]
        # 交换过后,转到下一次循环的结果
        # arr[i] < pivot, arr[j] > pivot

根据上面代码块的 L5L9 可知,arr[j] > pivot 不符合 arr[x] < arr[lo] 的条件,不可进行交换。

观察可发现,先从左往右,得到的跳出点的值 arr[x] > pivot,如果交换的话可以往右交换,也就是说,基准点可以选择 arr[hi],这样结果就是对的了。

关于随机基准点

def qsort(arr, lo, hi):

    if lo >= hi:
        return

    i = lo
    j = hi

    # 以此方式先随机选一个下标,再交换到 lo(hi)
    # 数值随机了,下面程序还是相当于使用 lo(hi),无需进行改动
    rand = random.randint(lo, hi)
    arr[lo], arr[rand] = arr[rand], arr[lo]

    # 选择 lo 为基准点
    pivot = arr[lo]

    while i < j:

        while i < j and arr[i] <= pivot:
            i += 1

        while i < j and arr[j] >= pivot:
            j -= 1
        
        arr[i], arr[j] = arr[j], arr[i]
    
    arr[lo], arr[j] = arr[j], arr[lo]

    qsort(arr, lo, i - 1)
    qsort(arr, i + 1, hi)

结论

基准点交换条件顺序
arr[lo]arr[x] < arr[lo]先从右往左找
arr[hi]arr[x] > arr[hi]先从左往右找
快速排序是一种高效的排序算法,它通过分治策略来将数组分为两部分,然后递归地对这两部分进行排序。这里是一个简单的使用C语言描述符(DSP)库编写快速排序的基本代码片段: ```c #include <stdio.h> #include <stdlib.h> void quick_sort(int arr[], int low, int high) { if (low < high) { int pivot = arr[high]; // 将最后一个元素作为基准 int i = (low - 1); // 定义小于基准的元素索引 for (int j = low; j <= high - 1; j++) { if (arr[j] < pivot) { // 如果当前元素比基准小 i++; // 移动较小元素的索引 swap(&arr[i], &arr[j]); // 交换两个位置的元素 } } swap(&arr[i + 1], &arr[high]); // 将基准放到正确的位置 quick_sort(arr, low, i); // 对基准左边的部分继续排序 quick_sort(arr, i + 2, high); // 对基准右边的部分继续排序 } } // 交换函数 void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } int main() { int data[] = {5, 3, 8, 6, 7, 2, 9, 4, 1}; int n = sizeof(data) / sizeof(data[0]); quick_sort(data, 0, n - 1); printf("Sorted array: "); for (int i = 0; i < n; i++) printf("%d ", data[i]); printf("\n"); // 关于按键1触发外部中断和流水灯操作,这通常涉及到硬件编程和实时操作系统环境,例如μC或嵌入式系统。 // 当按键1按下时,一般会设置中断服务程序(ISR),该程序会被中断处理机制自动调用。在这个ISR中,你需要检查按键状态并控制LED灯的状态,比如用定时器或者中断计数器配合来控制灯的亮灭顺序和速度。 return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lixifun

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

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

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

打赏作者

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

抵扣说明:

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

余额充值