一、选择排序
(尊重原创,转载请注明出处。)
选择排序是一种速度较慢的排序算法,运行时间为O(n2),这里只进行简单的逻辑分析和实现。大致是对待分析的数组(或者其它数据结构)尽心一遍遍的遍历,每次都找出里头最大或最小的那个,然后放入一个新的数组。实现如下:
def find_max(list):
max = list[0]
index = 0;
for i in range(1,len(list)):
if list[i] >= max:
max = list[i]
index = i
return index
def sort_list(list):
new_list = []
while len(list) > 0:
index = find_max(list)
new_list.append(list.pop(index))
return new_list
print sort_list([3,2,4,3,7,6,0,7,6,3]);
二、快速排序(最快的排序算法之一,D&C的典范)
1. 在将快速排序之前,我们先讨论一种著名的递归式问题解决方法——分而治之(D&C)。
D&C策略的包括两个步骤:
a. 找出一个尽可能简单的基线条件。
b. 不断的将问题分解,缩小规模,使其越来越靠近基线条件,直到最终符合基线条件。
2. 快速排序的逻辑分析
a. 先在数组中找一个元素作为基准值。(注意,基准值的选择直接影响了快速排序的性能高度,后面再讨论,这里先将第一个元素作为基准值)然后将数组中所有小于它的元素放在左边,大于它的元素放在右边。(这里是从小到大排序,反之)。
b. 对左右两边的元素分别再选出基准值,在分成两边。
c. 不断重复该步骤。
d. 当某边的元素个数为1个或为0个时,则该处达到基线条件。
e. 当所有的边都达到基线条件时,排序完成。
3. 分析——快速排序的实现思想正是使用了D&C策略
a. 首先找到了基线条件,某个数组的个数为1或为0时,该序列为排好的顺序。
b. 当数组个数为3个时,取出一个,进行对分,剩下的两个就满足了。
c. 4个时,可能会出现一边为0,一边为3的情况,再将3的那边进行对分,又可以满足了。
d. 以此类推,不管数组有多长,只要对整个数组进行不断的对分,减小规模,最后总能满足基线条件,达到排序的目的。
以上的的证明方式即为归纳证明。
4. 归纳证明
归纳证明是一种证明算法可行性的方式。包括两个步骤:基线条件和归纳条件。
即先证明对0个或者1个元素是有效的(基线条件),然后在证明如果0个或者1个元素有效,那么3个、4个、5个.........任意长度都有效(归纳条件)。
归纳证明经常与D&C策略配合使用。
5.基准值的选择
我们知道,选择排序的运行时间为O(n2),而快速排序的运行时间平均情况下为O(n*log n),最糟情况下为O(n2)。
a. 最糟情况:当我们每次都选择第一个数作为基准值的时候,调用栈的高度将是n,所以在栈调用上的花费的运行时间为O(n),且每次调用的运行时间为O(n),于是算法最终的运行时间为:O(n)*O(n)=O(n2)。
b. 平均情况:如果我们每次都尽量的取靠近中间的值,那么调用栈的高度将是log n,所以在栈调用上花费的运行时间为O(log n),又因为每次调用的运行时间为O(n),于是算法最终的运行时间为:O(n)*O(log n)=O(n*log n)。
其实只要我们每次都随机取一个元素作为基准值,就可以达到平均情况。下面贴上最糟情况的实现:
def quick_sort(list):
if len(list) < 2:
return list;
else:
v_num = list[0];
v_front = [];
v_behind = [];
for i in list[1:] :
if i <= v_num:
v_front.append(i);
else:
v_behind.append(i);
return quick_sort(v_front) + [v_num] + quick_sort(v_behind);
print quick_sort([3,5,1,2,6,8,7,0,123,213,432,21]);