关于快速排序:快速排序及其优化超详细解答代码真正理解

原文: https://zhuanlan.zhihu.com/p/...欢送关注我知乎号: https://www.zhihu.com/people/... 疾速排序QuickSort采纳了分治法Divide-and-ConquerMethod,通过将数组链表或其余元素集分为待排序汇合和已排序汇合,并在一次次迭代中将待排序汇合的元素转化到已排序汇合当中直到全副元素都为已排序则实现排序。 疾速排序利用这一策略,节约了解决已排序元素的老本。算法只关注残余待排序的元素,其中地位间断的未排序元素子串又分为:S1(左侧),pivot(替换枢纽元),S2(右侧) 以疾速排序来实现升序排序为例: 先从数组当选取出一个数组作为枢轴元pivot。(选取枢纽元的策略很要害)将待排序汇合所有小于枢轴元pivot的元素移至s1(左侧),所有大于枢轴元pivot的元素移至s2(右侧):(双指针中的相向指针法)先将枢轴元pivot(准有序局部)放到最左或者最右辨别出待排序局部。将i, j别离指向待排序局部索引的最左和最右。如果i索引指向的元素小于枢纽元,则i++;否则,i进行。j索引指向的元素大于枢纽元,j--;否则,j进行。如果i<j,则替换两个元素,持续循环3,4步骤;否则跳出循环,将i对应的元素与枢纽元pivot替换(这时候实现了宰割)这个时候定义枢轴元pivot为已排序局部,对残余未排序局部S1(左侧)S2(右侧)持续循环步骤1,2,3直到所有元素都已排序。最原始的疾速排序最原始的疾速排序采纳最简略粗犷的枢纽元选取策略,即选取第一个或最初一个元素为pivot。 该计划的均匀工夫复杂度为O(nlogn),当遇到数组刚好有序的状况下会呈现最坏工夫复杂度O(n^2),因为当输出序列自身有序时,会导致S1或S2汇合为空,除枢纽元外所有元素都在S1或S2,此时遍历替换的工夫效率大幅降落,因为节约了另一个区间的作用。 Python代码 nums = [23,2,4,6,2,5,1,6,13,54,8]def quicksort(nums, left, right): # left为最左索引,righ为最右索引 if left >= right: return pivot = left //取第一个元素为pivot i, j = left, right while i < j: while nums[pivot] <= nums[j] and i < j: j -= 1 while nums[pivot] >= nums[i] and i < j: i += 1 if i < j: nums[i], nums[j] = nums[j], nums[i] nums[pivot], nums[i] = nums[i], nums[pivot] quicksort(nums, left, i-1) quicksort(nums, i+1, right)def quicksort2(nums, left, right): # 用栈代替递归 if left >= right: return stack = [] while stack or left < right: if left < right: pivot = left i, j = left, right while i < j: while nums[pivot] <= nums[j] and i < j: j -= 1 while nums[pivot] >= nums[i] and i < j: i += 1 if i < j: nums[i], nums[j] = nums[j], nums[i] nums[pivot], nums[i] = nums[i], nums[pivot] stack.append((left, i, right)) right = i - 1 else: left, mid, right = stack.pop() left = mid + 1quicksort2(nums, 0, len(nums)-1)print(nums) 因为堆栈存储(递归等价于栈代替),疾速排序空间复杂度O(logn) ...

February 26, 2022 · 4 min · jiezi

关于快速排序:快速排序的简介以及随机三数中值优化

介绍快排的思维是,选取数组中一个数,作为基准(pivot),而后将数组分成左右两局部。右边局部全副小于基准,左边局部全副大于基准。随后再去递归地对右边局部和左边局部做雷同操作,直至数组被宰割成繁多一个元素,排序实现。网络上大部分快排的实现都是递归实现,这里给出模板。 // 快排模板func QuickSort(nums []int, leftEnd, rightEnd int) { if leftEnd < rightEnd { mid := Partition(nums, leftEnd, rightEnd) QuickSort(nums, leftEnd, mid-1) QuickSort(nums, mid+1, rightEnd) }}快排函数接管数组(nums)、左边界(leftEnd)、右边界(rightEnd)。左边界必须小于右边界。数组传入后,先通过Partition()函数分区。数组和左右边界被传入Partition()后,数组会被辨别左右两局部,右边的元素都小于左边的元素,Partition()会返回一个数值mid代表分区左右的基准值的下标。随后通过递归,再对基准数左右两边作出同样操作。 快排的要害算法,在于Partition()的设计。优化的要害,大部分在于基准数的选取。 实现东尼霍尔的实现疾速排序是东尼霍尔(Tony Hoare)设计的,他设计的算法思路,是选取要排序局部的第一个元素的作为基准。而后有左右两个指针从左右往两头凑近。右指针遇到比基准小的元素,会停下。左指针遇到比基准大的元素,会停下。两个指针进行后,会替换左右指针的元素,随后持续往两头后退,直至两个指针重合。而后再把最右边的基准值插入指针所在位置。 // 霍尔分区,采纳最右边的元素作为基准func HoarePartition(nums []int, leftEnd, rightEnd int) int { pivot := nums[leftEnd] l := leftEnd r := rightEnd for l < r { for l < r && nums[r] >= pivot { r-- } for l < r && nums[l] <= pivot { l++ } nums[l], nums[r] = nums[r], nums[l] } nums[leftEnd], nums[l] = nums[l], nums[leftEnd] return l}这个实际上也是大部分人写的版本。 ...

July 21, 2021 · 2 min · jiezi