快速排序
来源:互联网 发布:苹果越狱软件源大全 编辑:程序博客网 时间:2024/06/07 09:51
相关的快速排序的解释很容易找到,这里只贴代码
传统的快速排序,不添加任何优化
#!/usr/bin/python#coding: utf-8def Partition(res, low, high): # 把第一个元素作为基准点 point = res[low] while low < high: # 把小于基准点的元素都放在基准点的左边,大于基准点的元素都放在基准点的右边 # 接下来的两个while循环中的 <= 和 >= 中不能把等号去掉,如果去掉的话对相同的元素不能排序,在while循环中将死循环 # 从后往前遍历,把小于基准点的元素往前移 while low < high and res[high] >= point: high -= 1 # 元素交换 res[low], res[high] = res[high], res[low] # 从前往后遍历,把大于基准点的元素往后移 while low < high and res[low] <= point: low += 1 # 元素交换 res[low], res[high] = res[high], res[low] # 返回中间点的位置 return lowdef Quick_Sort(res, low, high): if low < high: # 获取基准点 point = Partition(res, low, high) # 递归调用基准点的左边 Quick_Sort(res, low, point - 1) # 递归调用基准点的右边 Quick_Sort(res, point + 1, high)if __name__ == "__main__": res = [51, 45, 15, 78, 84, 51, 24, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [51, 45, 15, 78, 84, 15, 85, 51] Quick_Sort(res, 0, len(res) - 1) print res
当前的快速排序虽然能够实现对序列的排序,但是仍然有些不足之处,因为每一次取的这个元素并不一定是中间的元素,不会使左右两边恰好有相等的元素。
举例如下:
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
这样一个序列,在排序过程中,取第一个元素作为基准点,这样经过一轮排序之后,
[0, 8, 7, 6, 5, 4, 3, 2, 1, 9]
只完成了一组元素的排序,效率并不高。
而且需要10趟快速排序才能得到最后的结果
每一趟的结果如下:
[0, 8, 7, 6, 5, 4, 3, 2, 1, 9]
[0, 8, 7, 6, 5, 4, 3, 2, 1, 9]
[0, 1, 7, 6, 5, 4, 3, 2, 8, 9]
[0, 1, 7, 6, 5, 4, 3, 2, 8, 9]
[0, 1, 2, 6, 5, 4, 3, 7, 8, 9]
[0, 1, 2, 6, 5, 4, 3, 7, 8, 9]
[0, 1, 2, 3, 5, 4, 6, 7, 8, 9]
[0, 1, 2, 3, 5, 4, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
对传统快速排序进行第一次优化
使用三数取中法。
三数取中法:从开头选一个元素,结尾选一个元素,中间选一个元素,取三个元素中间的那一个作为基准点(第一个元素)进行递归,最小的元素放在中间,最大的元素放在结尾。
代码如下(使用三数取中法):
#!/usr/bin/python#coding: utf-8def Partition(res, low, high): # 三数取中法进行优化 # 获取中间的那个数的下表 m = low + (high - low) / 2 if res[low] > res[high]: res[low], res[high] = res[high], res[low] if res[m] > res[high]: res[m], res[high] = res[high], res[m] # 经过前面的两步讨论,已经使high点索引的元素变成最大的 # 接下来把low点索引的元素变成m和low这两个索引点的最大值 if res[m] > res[low]: res[m], res[low] = res[low], res[m] point = res[low] while low < high: # 把小于基准点的元素都放在基准点的左边,大于基准点的元素都放在基准点的右边 # 接下来的两个while循环中的 <= 和 >= 中不能把等号去掉,如果去掉的话对相同的元素不能排序,在while循环中将死循环 # 从后往前遍历,把小于基准点的元素往前移 while low < high and res[high] >= point: high -= 1 # 元素交换 res[low], res[high] = res[high], res[low] # 从前往后遍历,把大于基准点的元素往后移 while low < high and res[low] <= point: low += 1 # 元素交换 res[low], res[high] = res[high], res[low] # 返回中间点的位置 return lowdef Quick_Sort(res, low, high): if low < high: # 获取基准点 point = Partition(res, low, high) # 递归调用基准点的左边 Quick_Sort(res, low, point - 1) # 递归调用基准点的右边 Quick_Sort(res, point + 1, high)if __name__ == "__main__": res = [51, 45, 15, 78, 84, 51, 24, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [51, 45, 15, 78, 84, 15, 85, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] Quick_Sort(res, 0, len(res) - 1) print res
其中新增代码为:
# 三数取中法进行优化 # 获取中间的那个数的下表 m = low + (high - low) / 2 if res[low] > res[high]: res[low], res[high] = res[high], res[low] if res[m] > res[high]: res[m], res[high] = res[high], res[m] # 经过前面的两步讨论,已经使high点索引的元素变成最大的 # 接下来把low点索引的元素变成m和low这两个索引点的最大值 if res[m] > res[low]: res[m], res[low] = res[low], res[m]
经过优化以后只需要6次就能得到最终的结果
每一趟的结果如下:
[1, 2, 3, 4, 0, 5, 6, 7, 8, 9]
[0, 1, 2, 4, 3, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
相对于第一种来说,进行了一定的优化。
对传统快速排序进行第二次优化(优化掉不必要的赋值)
在Partiton函数中,每一次找出不满足条件的都要进行一次交换,但是对于基准点来说,在每一趟中都要进行多次的交换,这样会消耗掉部分的时间,可以通过直接赋值的方式来进行相应的优化。
即对于需要交换的两个元素,如果不是基准点的话,直接把该元素赋值到相应的位置,基准点不进行赋值。
代码如下:
#!/usr/bin/python#coding: utf-8def Partition(res, low, high): # 把第一个元素作为基准点 point = res[low] while low < high: # 把小于基准点的元素都放在基准点的左边,大于基准点的元素都放在基准点的右边 # 接下来的两个while循环中的 <= 和 >= 中不能把等号去掉,如果去掉的话对相同的元素不能排序,在while循环中将死循环 # 从后往前遍历,把小于基准点的元素往前移 while low < high and res[high] >= point: high -= 1 # 元素交换 res[low] = res[high] # 从前往后遍历,把大于基准点的元素往后移 while low < high and res[low] <= point: low += 1 # 元素交换 res[high] = res[low] res[low] = point # 返回中间点的位置 return lowdef Quick_Sort(res, low, high): if low < high: # 获取基准点 point = Partition(res, low, high) # 递归调用基准点的左边 Quick_Sort(res, low, point - 1) # 递归调用基准点的右边 Quick_Sort(res, point + 1, high)if __name__ == "__main__": res = [51, 45, 15, 78, 84, 51, 24, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [51, 45, 15, 78, 84, 15, 85, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] Quick_Sort(res, 0, len(res) - 1) print res相比于传统的快速排序,只是对Partition函数进行了优化,优化部分如下:
def Partition(res, low, high): # 把第一个元素作为基准点 point = res[low] while low < high: # 把小于基准点的元素都放在基准点的左边,大于基准点的元素都放在基准点的右边 # 接下来的两个while循环中的 <= 和 >= 中不能把等号去掉,如果去掉的话对相同的元素不能排序,在while循环中将死循环 # 从后往前遍历,把小于基准点的元素往前移 while low < high and res[high] >= point: high -= 1 # 元素交换 res[low] = res[high] # 从前往后遍历,把大于基准点的元素往后移 while low < high and res[low] <= point: low += 1 # 元素交换 res[high] = res[low] res[low] = point # 返回中间点的位置 return low
经过这种优化方式以后,并不会对快速排序所需要的次数有所减少,只是减少了不必要的赋值操作。
对传统快速排序进行第三次优化(优化小数组时的排序方案)
对于一些数据量较小的情况,没有必要进行快速排序,因为这样并不会对时间有太好的优化,反而会消耗大量的内存,可以使用直接插入排序(因为直接插入排序是简单排序中性能最好的)。
给定一个数组,何时选择直接插入排序?何时选择快速排序?
如果说数组的长度小于7,使用直接插入排序是最好的;如果大于等于7,则选择快速排序,因为数组长度为7是快速排序效率最高的分界点。
#!/usr/bin/python#coding: utf-8def ISort(res, low, high): def Insert_Sort(res, num): u"直接插入排序" for i in range(1, num): # 如果说当前元素比上一个元素小,则进行交换,把当前的 res[i] 插入到相应的位置 if res[i] < res[i - 1]: temp = res[i] j = i - 1 # 继续往前遍历,如果说当前位置的值大于temp的话,则把当前值后移一位 # while语句退出的条件是li列表越界或者是当前j位置的值大于temp while j >= 0 and res[j] > temp: res[j + 1] = res[j] j -= 1 # 当前j所表示的是所能放temp的前一位, 所以要加1 res[j + 1] = temp return res # 把排序的序列重新赋值给res res[low : ] = Insert_Sort(res[low : ], high - low + 1)def Partition(res, low, high): point = res[low] while low < high: # 把小于基准点的元素都放在基准点的左边,大于基准点的元素都放在基准点的右边 # 接下来的两个while循环中的 <= 和 >= 中不能把等号去掉,如果去掉的话对相同的元素不能排序,在while循环中将死循环 # 从后往前遍历,把小于基准点的元素往前移 while low < high and res[high] >= point: high -= 1 # 元素交换 res[low], res[high] = res[high], res[low] # 从前往后遍历,把大于基准点的元素往后移 while low < high and res[low] <= point: low += 1 # 元素交换 res[low], res[high] = res[high], res[low] # 返回中间点的位置 return lowdef Quick_Sort(res, low, high): if high - low > 7: # 获取基准点 point = Partition(res, low, high) # 递归调用基准点的左边 Quick_Sort(res, low, point - 1) # 递归调用基准点的右边 Quick_Sort(res, point + 1, high) else: # 调用直接插入排序 ISort(res, low, high)if __name__ == "__main__": res = [51, 45, 15, 78, 84, 51, 24, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [51, 45, 15, 78, 84, 15, 85, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] Quick_Sort(res, 0, len(res) - 1) print res
这种优化方案个人并不推荐,代码量增加太多,
就是把直接插入排序和快速排序结合起来即可。
对传统快速排序进行第四次优化(优化递归操作)
通过减少递归调用的次数(尾递归的方式),优化空间的利用率。
#!/usr/bin/python#coding: utf-8def Partition(res, low, high): point = res[low] while low < high: # 把小于基准点的元素都放在基准点的左边,大于基准点的元素都放在基准点的右边 # 接下来的两个while循环中的 <= 和 >= 中不能把等号去掉,如果去掉的话对相同的元素不能排序,在while循环中将死循环 # 从后往前遍历,把小于基准点的元素往前移 while low < high and res[high] >= point: high -= 1 # 元素交换 res[low], res[high] = res[high], res[low] # 从前往后遍历,把大于基准点的元素往后移 while low < high and res[low] <= point: low += 1 # 元素交换 res[low], res[high] = res[high], res[low] # 返回中间点的位置 return lowdef Quick_Sort(res, low, high): while low < high: # 获取基准点 point = Partition(res, low, high) if point - low < high - point: # 递归调用基准点的左边 Quick_Sort(res, low, point - 1) low = point + 1 else: # 递归调用基准点的右边 Quick_Sort(res, point + 1, high) high = point - 1if __name__ == "__main__": res = [51, 45, 15, 78, 84, 51, 24, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [51, 45, 15, 78, 84, 15, 85, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] Quick_Sort(res, 0, len(res) - 1) print res修改的代码如下:
def Quick_Sort(res, low, high): while low < high: # 获取基准点 point = Partition(res, low, high) if point - low < high - point: # 递归调用基准点的左边 Quick_Sort(res, low, point - 1) low = point + 1 else: # 递归调用基准点的右边 Quick_Sort(res, point + 1, high) high = point - 1
四步优化中前面三步可以在一块使用,第四种不能和第三种在写在同一个代码中
第一二三种优化放一块的代码如下:
#!/usr/bin/python#coding: utf-8def ISort(res, low, high): def Insert_Sort(res, num): u"直接插入排序" for i in range(1, num): # 如果说当前元素比上一个元素小,则进行交换,把当前的 res[i] 插入到相应的位置 if res[i] < res[i - 1]: temp = res[i] j = i - 1 # 继续往前遍历,如果说当前位置的值大于temp的话,则把当前值后移一位 # while语句退出的条件是li列表越界或者是当前j位置的值大于temp while j >= 0 and res[j] > temp: res[j + 1] = res[j] j -= 1 # 当前j所表示的是所能放temp的前一位, 所以要加1 res[j + 1] = temp return res # 把排序的序列重新赋值给res res[low : ] = Insert_Sort(res[low : ], high - low + 1)def Partition(res, low, high): # 三数取中法进行优化 # 获取中间的那个数的下表 m = low + (high - low) / 2 if res[low] > res[high]: res[low], res[high] = res[high], res[low] if res[m] > res[high]: res[m], res[high] = res[high], res[m] # 经过前面的两步讨论,已经使high点索引的元素变成最大的 # 接下来把low点索引的元素变成m和low这两个索引点的最大值 if res[m] > res[low]: res[m], res[low] = res[low], res[m] point = res[low] while low < high: # 把小于基准点的元素都放在基准点的左边,大于基准点的元素都放在基准点的右边 # 接下来的两个while循环中的 <= 和 >= 中不能把等号去掉,如果去掉的话对相同的元素不能排序,在while循环中将死循环 # 从后往前遍历,把小于基准点的元素往前移 while low < high and res[high] >= point: high -= 1 # 元素赋值 res[low] = res[high] # 从前往后遍历,把大于基准点的元素往后移 while low < high and res[low] <= point: low += 1 # 元素赋值 res[high] = res[low] # 返回中间点的位置 return lowdef Quick_Sort(res, low, high): if high - low > 7: # 获取基准点 point = Partition(res, low, high) # 递归调用基准点的左边 Quick_Sort(res, low, point - 1) # 递归调用基准点的右边 Quick_Sort(res, point + 1, high) else: # 调用直接插入排序 ISort(res, low, high)if __name__ == "__main__": res = [51, 45, 15, 78, 84, 51, 24, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [51, 45, 15, 78, 84, 15, 85, 51] Quick_Sort(res, 0, len(res) - 1) print res res = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] Quick_Sort(res, 0, len(res) - 1) print res
可根据具体问题选择多种合并的优化方案,自行测试即可。
- 快速排序
- 快速排序
- 快速排序
- 快速排序!
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- 快速排序
- [整理] 浏览器差异(收集)
- java与c的不同
- 【C++Primer】封装_拷贝构造函数
- 第二十七课:二阶构造模式----------狄泰软件学院
- c# md5 加密
- 快速排序
- c语言 线性表的生成代码
- 选择排序法
- 关于mysql修改root密码
- 静态链接之目标文件的内容
- 第二十八课:友元的尴尬能力----------狄泰软件学院
- 众数问题
- 文件访问权限控制列表FACL及特殊权限
- 【S5PV210学习笔记】小错误引发大问题(2)