快找工作了,该复习算法。这里总结一下排序算法。供以后查询
1、快排
当初研一的时候,老师留的实验作业就是写快速排序。其中pivot设为随机选择,与非随机选择。然后比较性能。
实际上做完实验,发现合理的pivot会导致 很平衡 的划分。我们需要故意设置一个有序的序列(对应快排最坏的情况),选择400000个int。性能还是差很多。将近10倍吧。
快排的思想 result = 【low】+pivot+【high】
然后对两个子区间【low】、【high】分别递归调用即可。
时间复杂度O(nlogn)。最好O(nlogn),最坏O(N2)。
空间复杂度:依情况而定,有的是原址排序,有的是非原址的,有的是递归调用,有的是循环实现。
【段子】当初黄老师问起时间复杂度:我们都说是O(nlogn)。他:“”错”。还有O(n2)的情况。遭到批评:“大学算法谁教的?!”当然:快排之所以叫快排,是因为:只要不是待排序的数组有序,那么一定能达到很好的性能,上课他又举例,还真是。而且nlogn它前面的系数很低。平均性能好像是1.44。
像nlogn的排序有好几个:堆排序,归并排序等。但是一般都弄不过快排。尤其是待排数组长度很大的时候。
def partition(seq):
pivot,seq= seq[0],seq[1:]
low=[x for x in seq if x<= pi] #小于pivot放左边
hi=[x for x in seq if x> pi] #大于pivot放右边
return low,pivot,hi
def quicksort(seq):
if len(seq) <= 1: #递归 base case
return seq
low,pivot,hi=partition(seq) #进行一次划分
return quicksort(low)+[pivot]+quicksort(hi) #递归划分
2、堆排序
谈到堆排序,我第一反应就是,有了快排干嘛用堆排序??!
实际 上解决大数据top-K的问题,他就派上了用场。
1-堆是一个完全二叉树,因此可以用连续内存表示。堆可分为:大根堆(堆顶元素最大),小根堆(堆顶元素最小)。
2-堆的表示:用数组(c),或者vector(c++)
数组的【】【2】【4】【3】【1】
3-这里空出第一个位置。就可以满足对于节点 i 他的父亲是 i/2。他的儿子 2i (左),2i+1 (右)。
4-建堆(n个元素)的时间为O(n)
堆排序过程(大根堆为例)
1- 将待排序的序列构造成一个大顶堆 #此时堆顶为最大
2- 将它移走(其实就是将它与堆数组的末尾元素交换,此时末尾元素就是最大值)。
3- 此时,需要维护前n-1个元素堆的性质(堆调整)。第n个元素已经有序
4- 调整完,此时又是一个n-1个元素的大根堆,再取堆顶与堆中最后一个元素交换。
5- 此时,前需要维护数组前n-2个元素的堆性质(调整堆)。第n-1与第n个元素已经有序。
如此反复执行,便能得到一个有序序列了。
结论:大根堆,排序结果是升序,小根堆排序结果是降序
演示:http://www.benfrederickson.com/heap-visualization/
# 调整堆
def adjust_heap(lists, i, size):
lchild = 2 * i + 1
rchild = 2 * i + 2
max = i
if i < size / 2:
if lchild < size and lists[lchild] > lists[max]:
max = lchild
if rchild < size and lists[rchild] > lists[max]:
max = rchild
if max != i:
lists[max], lists[i] = lists[i], lists[max]
adjust_heap(lists, max, size)
# 创建堆
def build_heap(lists, size):
for i in range(0, (size/2))[::-1]:
adjust_heap(lists, i, size)
# 堆排序
def heap_sort(lists):
size = len(lists)
build_heap(lists, size)
for i in range(0, size)[::-1]:
lists[0], lists[i] = lists[i], lists[0]
adjust_heap(lists, 0, i)
那么开始提出 top-k 的问题如何做呢?
假设百度每天的热搜词有log文件。这个文件巨大,几十g(其中有相同的词)。找出某天top100。
先用hash_map,构建 < key=[词汇],value=[次数] > pair。构建之后我们需要对这个集合进行排序。先遍历前100个pair。构建最小堆。然后每次过来一个数就跟堆顶相比较,如果比堆顶大,则替换掉堆顶。进行一次堆调整。
这样我们发现:把数据存在磁盘上就可以比较。不用把待比较的元素都加载到内存中。
3、归并排序:
def merge(left, right):
i, j = 0, 0
result = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
return result
def merge_sort(lists):
# 归并排序
if len(lists) <= 1:
return lists
num = len(lists) / 2
left = merge_sort(lists[:num])
right = merge_sort(lists[num:])
return merge(left, right)