《算法图解》之快速排序

以前一章的递归为基础,讲述“分而治之”,即D&C。重点讲述二分查找的基础——快速排序。

1 分而治之

分而治之(divide and conquer, D&C)一种著名的递归式问题解决方法.

demo1

假设你是农场主,有一小块土地,你要将这块土地均匀地分成方块,且分出的方块要尽可能大.

def land(x, y):
    if (x == y): # 基线条件
        return x
    else: # 循环条件
        z = x - y
        if z > y:
            x = z
        else:
            x = y
            y = z
        return land(x, y)

print(land(1680, 640)) # 80

demo2

给定一个数字数组,将这些数字相加,并返回结果.

# 使用循环
def sum(arr):
    total = 0
    for x in arr:
        total += x
	return total

print(sum([1, 2, 3, 4])) # 10

# 使用递归
def sum(arr, i, ans):
    if len(arr) > i:
        return sum(arr, i+1, ans + arr[i])
	else:
        return ans
    
print(sum([1, 2, 3, 4], 0, 0)) # 10

2 快速排序

基准值:pivot

分区:partitioning

def quicksort(array):
    if len(array) < 2:
        return array
    else:
        pivot = array[0] # 将第一个元素作为基准值
        less = [i for i in array[1:] if i <= pivot] # 小于基准值的数组
        greater = [i for i in array[1:] if i > pivot] # 大于基准值的数组
        return quicksort(less) + [pivot] + quicksort(greater)
    
print(quicksort([10, 5, 2, 3]))
print(quicksort([3, 5, 2, 1, 4]))

3 再谈大O表示法

3.1 比较合并排序和快速排序

快速排序在平均情况下运行时间为O(nlogn),而合并排序的运行时间总是O(nlogn),为何不使用合并排序?

因为它们的常量不一样,假设快速排序每一步1毫秒,合并排序的每一步需要1秒

3.2 平均情况和最糟情况

快速排序的性能高度依赖于选择的基准值

最糟情况:

一般情况:

平均情况:

4 小结

  • D&C将问题逐步分解,使用D&C处理列表时,基线条件很可能是空数组或只包含一个元素或数组
  • 实现快速排序时,请随机地选择用作基准值的元素,快速排序的平均运行时间为O(nlogn)
  • 大O表示法中的常量有时候很重要,这就是快速排序比合并排序快的原因
  • 在比较二分查找和简单查找时,常量无关紧要,因为列表很长时,O(logn)比O(n)快得多
posted @ 2018-07-09 04:32  okokabcd  阅读(436)  评论(0编辑  收藏  举报