算法图解之快速排序
快速排序比选择排序要快得多,采用分而治之的思想,具体实现是用递归。
1. 基线条件
数组为空或只包含一个元素
2. 递归条件
将数组分解,直到满足基线条件
3. 工作原理
- 先从数组中选择一个元素,这个元素我们称之为基准值(pivot)。
- 找出比基准值小的值放在基准值左边以及比基准值大的值放在基准值右边。
- 递归左边和右边的元组,直到都满足基线条件后,从栈最高层开始返回。
4. 代码实现
1 def quicksort(array): 2 if len(array) < 2: 3 return array 4 pivot = array[0] 5 6 lesser = [item for item in array[1:] if item <= pivot] 7 greater = [item for item in array[1:] if item > pivot] 8 9 return quicksort(lesser) + [pivot] + quicksort(greater) 10 11 print(quicksort([5,10,23,222,3,0,12,412,4]))
5. 合并排序和选择排序
这里要说一下合并排序,运行时间为O(n log n)。而快速排序在最糟糕的情况下的运行时间是O(n 2 ),与选择排序一样。
但是我们还是要用快速排序,因为快速排序的平均时间是O(n log n)。而合并序有一个常量,等于每次计算都会加上一个常量,所以实际上快速排序速度更快,前提这两种算法的大O运行时间差不多。如果是两种算法的大O运行时间不同,这样常量将无关紧要。比如二分查找和简单查找。
6.快速排序的平均情况和最糟情况
只有每次将第一个元素作为基准值,才会出现最糟糕的情况O(n 2 )。最好的情况O(log n)是每次基准值都是中间值。
当层数为O(log n)(用技术术语说,调用栈的高度为O(log n)),而每层需要的时间为O(n)。因此整个算法需要的时间为O(n) * O(log n) = O(n log n)。这就是最佳情况。
在最糟情况下,有O(n)层,因此该算法的运行时间为O(n) * O(n) = O(n 2 )。
7. 复杂版本
def partition(array, left, right): pivot = array[left] while left < right: # 当右边没有比pivot小的数,right就一直走到和left重合了,下面这个条件是无法退出循环的,right会一直-1 # while array[right] >= pivot: while left < right and array[right] >= pivot: # 从右边找比pivot小的数 right -= 1 # 往左走一步 # 如果left已经和right重合了。那么array[left]=array[right]就是自己等于自己 array[left] = array[right] # 找到了比pivot小的数,把当前小的那个数放到最左边 print(array) # 下面的while的left<right有两个作用: # 1. 在上一个while循环的时候就可能找到了left = right了 # 2. 在自己while循环的过程中left=right了 while left < right and array[left] <= pivot: # 左边找比pivot大的数 left += 1 # 因为上面的right一直在往左移动,所以array[right]的位置是往左移动完的位置,并且它的值已经放到最左边了。 # 或者是和left重合的位置,这样array[right]=array[left]就是自己等于自己 array[right] = array[left] # 把左边的值写到右边空位上 array[left] = pivot # array[left]和array[right]相等了(也就是在中间了),把原来的值放回中间 return left # left和right都行,最后left和right碰上了 # left和right代表最左边和最右边的值 def quick_sort(array, left, right): if left < right: # left = right 就一个元素, left小于right至少有两个元素 mid = partition(array, left, right) quick_sort(array, left, mid - 1) quick_sort(array, mid + 1, right)