排序算法-快速排序

向右看齐 2024-05-02 20:26 39阅读 0赞

一、快速排序

快速排序也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的。

冒泡排序在每一轮中只把1个元素冒泡到数列的一端。而快速排序则在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分。

8fd7deab42234322a155f7351c83ecf8.png

这种思路就叫作分治法。

在分治法的思想下,原数列在每一轮都被拆分成两部分,每一部分在下一轮又分别被拆分成两部分,直到不可再分为止。 每一轮的比较和交换,需要把数组中的全部元素都遍历一遍,时间复杂度是O(n)。这样的遍历一共需要多少轮呢﹖假如元素个数是n,那么平均情况下需要logn轮,因此快速排序算法总体的平均时间复杂度是O ( nlogn)。

基准元素,英文pivot,在分治过程中,以基准元素为中心,把其他元素移动到它的左右两边。

1、双边循环法。

2、单边循环法。

1、双边循环法

首先,选定基准元素pivot,并且设置两个指针left和right,指向数列的最左和最右两个元素。

e3e376cb200a474991f0063e4bef3a4a.png

第1次循环,从right指针开始,让指针所指向的元素和基准元素做比较。如果right>=pivot,则指针向左移动(-1);如果right<pivot,则right指针停止移动(stop),切换到left指针。

在当前数列中,1<4,所以right直接停止移动,换到left指针,进行下一步行动。轮到left指针行动,让指针所指向的元素和基准元素做比较。如果left<=pivot,则指针向右移动(+1);如果left>pivot,则left指针停止移动(stop)。

a765f133128e4c1e84eeba9bb19ae0cd.png

由于left开始指向的是基准元素,判断肯定相等,所以left右移1位。

7f3bed404fd146f6b4a951139dd52a93.png

由于7>4,left指针在元素7的位置停下。这时,让left指针和right指针所指向的元素进行交换。

eb8b97d548654e919cda0b8615064b2d.png

bc45b6943b0f404695b4988cb00f62ad.png

  1. def partition(start,end,ll):
  2. '''
  3. 比较交换过程
  4. :param start: 开始索引
  5. :param end: 结束索引
  6. :param ll: 数列
  7. :return: 左边索引
  8. '''
  9. #取第一个元素为基准元素,也可以随机
  10. pirot=ll[start]
  11. #左边元素索引
  12. left=start
  13. #右边元素索引
  14. right=end
  15. #循环
  16. while left!=right:
  17. #控制right指针比较,并左移
  18. while left<right and ll[right]>=pirot:
  19. right -=1
  20. # 控制left指针比较,并右移
  21. while left<right and ll[left]<=pirot:
  22. left+=1
  23. #交换left指针和right指针的元素
  24. if left<right:
  25. #交换
  26. ll[left],ll[right]=ll[right],ll[left]
  27. #pirot与left,right指针重合
  28. ll[start]=ll[left]
  29. ll[left]=pirot
  30. return left
  31. def quickSort(start,end,ll):
  32. #递归结束
  33. if start>=end:
  34. return
  35. #获取基准元素位置
  36. pirot=partition(start,end,ll)
  37. #根据基准元素,分成两部分递归排序
  38. quickSort(start,pirot-1,ll)
  39. quickSort(pirot+1,end,ll)
  40. if __name__ == '__main__':
  41. ll=[4,7,6,5,3,2,8,1]
  42. print('排序前:')
  43. print(ll)
  44. #调用方法排序
  45. quickSort(0,len(ll)-1,ll)
  46. print('排序后:')
  47. print(ll)

de0548bb0a484816980e2824c70a9648.png

2、单边循环法

只从数组的一边对元素进行遍历和交换。

开始和双边循环法相似,首先选定基准元素pivot。同时,设置一个mark指针指向数列起始位置,这个mark指针代表小于基准元素的区域边界。接下来,从基准元素的下一个位置开始遍历数组。

bb69d07f9a67473c93351cefd3895007.png

如果遍历到的元素>基准元素,就继续往后遍历。

如果遍历到的元素<基准元素,则需要做两件事:

第一,把mark指针右移1位,因为小于pivot的区域边界增大了1;

第二,让最新遍历到的元素和mark指针所在位置的元素交换位置,因为最新遍历的元素归属于小于pivot的区域。

首先遍历到元素7,7>4,所以继续遍历。

c772de5144da4c50a454fd4808a896e0.png

遍历到的元素是3,3<4,所以mark指针右移1位。

cb5f7938eb484a5ebdf4c5c84a669ce8.png

随后,让元素3和mark指针所在位置的元素交换,因为元素3归属于小于pivot的区域。

0bc89f3944ec4c15bc394182eb92a7c8.png 64d819c9205d4c2e918a944913e07b35.png

  1. def partitionSingle(start,end,ll):
  2. # 取第一个元素为基准元素,也可以随机
  3. pirot = ll[start]
  4. # 标记索引
  5. mark = start
  6. for i in range(start+1,end+1):
  7. #判断遍历的元素<基准元素
  8. if ll[i]<pirot:
  9. #右移1位
  10. mark+=1
  11. #交换
  12. p=ll[mark]
  13. ll[mark]=ll[i]
  14. ll[i]=p
  15. ll[start]=ll[mark]
  16. ll[mark]=pirot
  17. return mark
  18. def quickSort(start,end,ll):
  19. #递归结束
  20. if start>=end:
  21. return
  22. #获取基准元素位置
  23. pirot=partitionSingle(start,end,ll)
  24. #根据基准元素,分成两部分递归排序
  25. quickSort(start,pirot-1,ll)
  26. quickSort(pirot+1,end,ll)

很明显,partitionSingle方法只要一个循环就OK,的确比双边循环法简单多了。

发表评论

表情:
评论列表 (有 0 条评论,39人围观)

还没有评论,来说两句吧...

相关阅读

    相关 排序算法——快速排序

    排序算法——快速排序 > 快速排序通过一趟排序将待排序序列分隔成独立的两部分,其中一部分序列的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个

    相关 排序算法快速排序

    首先快速排序,数据结构学完之后,把一些排序只是懂思想,一直没有实现,今天花时间实现了一下 快速排序的思想就是每次从一段中随机选一个数,把这一段中比它小的元素放在这个元素的前

    相关 排序算法——快速排序

    前言 快速排序采用了分治法,即将原问题划分成为若干个规模更小且与原问题相似的子问题,然后递归地解决这些子问题,最后将他们组合起来。 快速排序的思想是:假设数据元素存放在

    相关 排序算法-快速排序

    快速排序 是最高效、不占用空间的一种排序算法 快排的精髓 是在于 找到 中间基数。 比中间基数小的放在左边 ,比中间基数大的放在右边 然后 左右各自进行快排。 参考

    相关 排序算法---快速排序

    基本思路: 快速排序,数组冲两边出发。 首先取一个关键字。 在第一次排序后。 大于和小于 关键字的各在 关键字两边。 然后在对两边 重复上面步骤,取关键字,排序。 直