快速排序【分治思想 + python实现 含图解】

文章目录

  • 前言
  • 一、什么分治思想
  • 二、快速排序图解
  • 三、快速排序代码逐行解析
    • 1.整体思路
    • 2.终止条件
    • 3.左右指针
    • 4.循环过程
    • 5.分治模块
  • 四、完整代码(不想听我啰里吧嗦的废话直接戳这里哦)


前言

前两天遇到了一道用快速排序来做的题,脑子里有思路但就是做不出来,总是因为一些边界条件排不对,其实快排我之前看过,但是,,,很久没接触就又忘记了,这次就来记录一下。


一、什么分治思想

首先,“分而治之”就是把一个大问题分解成很多个小问题,逐个把小问题解决掉。而具体的“小问题”有多小呢?举个例子,如果一个列表里只有一个元素,那么对这个列表做任何操作(排序?根本不用排,只有一个元素跟谁排,直接返回就行啦 >_<)都会非常轻松。
我们要做的就是将一个长列表分到很多个只有一个元素的短列表,同时在“分”的过程中做出合适的操作(这里是重点),最后将所有的短列表结果合在一起就是最终的答案。

二、快速排序图解

以列表nums1 = [1, 3, 2, 0, 4, 5, 6, 7, 9, 10, 8]为例:
快速排序【分治思想 + python实现 含图解】_第1张图片
设置第一个元素为pivot,进行第一轮查找:
快速排序【分治思想 + python实现 含图解】_第2张图片
第二轮查找:
快速排序【分治思想 + python实现 含图解】_第3张图片
第三轮,发现l>r,已经不符合循环条件了,交换r和pivot,可以发现此时的pivot左边都是不大于它的数,右边都是不小于它的数:
快速排序【分治思想 + python实现 含图解】_第4张图片

三、快速排序代码逐行解析

1.整体思路

1) 将一个列表整体排好序,说明列表中每一个元素都在自己正确的位置上(比如有列表 [2,3,1] 需要按升序排列,那么排好序后,元素3就一定要在第三个位置上)。
2) 反过来想,也就意味着,将列表中每一个元素都放到它该在的位置上,整个列表就排好序了。
3) 如何找到一个元素的正确位置?
4) 若为升序排序,则该元素的左边都是<=自己的元素,右边都是>=自己的元素;降序排序同理,左>=自己,右<=自己。
5)如果该元素满足上面的条件,那么不管其他元素是不是有序的,反正自己是排好了,自己就在这个位置不动了,其他元素就让他们自己排去吧。
6)最后,是快速排序需要传入的参数(列表,起始下标,终止下标):

def quick_sort(nums, start, end):

2.终止条件

就是上面说到的,如果列表中只有一个元素,不用排序,直接返回即可。也就是列表的起始 start = end 的情况;如果 start > end 是显然不符合的,至于为什么会出现这种情况,有两种,下面解释“分治模块”的第三条会提到。

    if start >= end:
        return

3.左右指针

选择一个要排的元素记为 pivot(看了好多大佬快排都用这个单词,我也用,这样就会显得我很专业吼吼吼,单词英文解释:支点; 枢轴; 核心),可以选择列表起始的元素 nums[start];设置左右双指针 l 和 r,并初始化为 start 和 end。

	pivot, l, r = nums[start], start, end

4.循环过程

在左指针小于右指针的前提下:
首先从右往左找到第一个比 pivot 小的元素:

    while l < r:
        while nums[r] >= pivot and l < r:
            r -= 1

再从左到右找到第一个比 pivot 大的元素:

        while nums[l] <= pivot and l < r:
            l += 1

当 l 和 r 都找到后,交换两个值:

        nums[l], nums[r] = nums[r], nums[l]

判断此时 l 和 r 的关系,如果仍然 l < r ,继续上述操作;否则跳出循环,交换之前选做 pivot 的 nums[start] 元素,将其与 下表为 r 的值进行交换:
(由于有 l < r 的约束,这一步最多有 i = j)

    nums[start], nums[r] = nums[r], nums[start]

5.分治模块

1)此时,下标为 r 的元素(就是之前选的 pivot)已经排在了正确的位置,左边都 <= pivot,右边都 >= pivot,那么我们再对左半部分 nums 和右半部分 nums 分别进行排序即可。
2)左边的起始下标仍为 start,终止下标变成了 r-1,因为 nums[r] 已经排好序不用再加上了;右边同理,起始为 r+1,终止为 end。
3)如果最后一轮排序找到的 r 就等于列表起始下标start,或者 r 就等于列表终止下标end,那么显然start > r-1 或者 r+1>end,则下一次的quick_sort 中有 start>end,所以终止条件中会有出现 start>end 时直接返回的这种情况。

	quick_sort(nums, start, r - 1)
    quick_sort(nums, r + 1, end)

四、完整代码(不想听我啰里吧嗦的废话直接戳这里哦)

def quick_sort(nums, start, end):
    if start >= end:
        return

    pivot, l, r = nums[start], start, end

    while l < r:
        while nums[r] >= pivot and l < r:
            r -= 1
        while nums[l] <= pivot and l < r:
            l += 1
        nums[l], nums[r] = nums[r], nums[l]

    nums[start], nums[r] = nums[r], nums[start]
    # print(start, end, l, r, nums)

    quick_sort(nums, start, r - 1)
    quick_sort(nums, r + 1, end)


def f(nums):
    quick_sort(nums, 0, len(nums) - 1)
    return nums


nums1 = [3, 2, 3, 1, 2, 4, 5, 5, 6]
print(f(nums1))


最后,quick_sort 代码中的 return 没有返回任何值,因为操作都是在列表 nums 上直接完成的。如果大家有什么看法和疑问,欢迎留言哦。

你可能感兴趣的:(排序算法,算法,数据结构)