递归排序核心

递归排序的核心是 分与合

分的最终结果 就是将原数组中每一个数字分作一个数组, 合就是 所有小数组不断排序,合并的过程。

合并的过程是先将两个含有一个数字的数组排序,合并(每次比较两个数组的最小值,将更小值加入新数组中,当有一个数组无元素,则将还有元素的数组追加到新数组中即完成排序,合并。新数组已是有序)为一个含有两个元素的有序数组。然后是两个含两个元素的有序数组排序合并为含四个元素的有序数组,两个含四个元素的有序数组合并为含八个元素的有序数组。一直到最终合并为一个数组即完成排序(当然其中有一个小数组可能不是含2的次方个数元素,但处理方式是一样的)。

为什么要用循环迭代实现

我在学归并排序时是先学的递归法实现,在看优化方法时有说到可以用循环迭代的方法实现效率会更高,然后就去了解循环迭代实现归并排序。

然而感觉能搜到的循环迭代法实现感觉不太能理解,至少我在有归并排序的理解在先的情况下看了十几分钟代码还不大懂其中过程。我就想能不能用比较容易理解的方式利用循环迭代实现归并排序。

然后就有了利用队列,用循环迭代的方法完成归并排序。该方法容易理解,也不必考虑些什么递归三要素。并且通过后文的简单的效率分析,能看出处理效率也很好。

循环迭代实现的整体思路

分,平时我们循环一个数组的时候,不就是不断取出其中每一个数字吗,这不是和归并排序中 分 的最终结果如出一辙吗。只要我们将循环中得到每一个数字做成一个含一个数字的数组加入队列中,即可完成分的过程。

合,只要队列中的元素不唯一(队列中每一个元素都是数组哦),不断从队首取出两个数组,排序合并成一个数组,加入到队尾。直至队列元素唯一,最终队列中的唯一数组即是排序好的数组。

搞清楚了整体思路,代码就呼之欲出了。

代码如下:

from _collections import deque

def que_merge(array):

merge_queue = deque()

# 开始分

# 此处是将原列表中元素pop出加入队列,循环结束后原列表会变成只剩一个数字。

# 若不想改变原列表用for循环将每一个数字变为列表加入队列即可。

while len(array) > 1:

# 列表弹出两个列表排序合并,这样进入队列的就直接是含两个数字的有序列表了。当然,不合并直接加入队列也是一样的,并不会影响合的过程。

one = array.pop()

other = array.pop()

if one < other:

merge_list = [one, other]

merge_queue.appendleft(merge_list)

else:

merge_list = [other, one]

merge_queue.appendleft(merge_list)

if array:

merge_queue.appendleft([array.pop()])

# 队列准备就绪,开始排序,合并

while len(merge_queue) > 1:

merge_list1 = merge_queue.pop()

merge_list2 = merge_queue.pop()

l = 0

r = 0

new_list = []

while l < len(merge_list1) and r < len(merge_list2):

if merge_list1[l] < merge_list2[r]:

new_list.append(merge_list1[l])

l += 1

else:

new_list.append(merge_list2[r])

r += 1

# 将两个列表剩余的元素追加到新列表中(有一个列表元素是空)。

new_list += merge_list1[l:]

new_list += merge_list2[r:]

merge_queue.appendleft(new_list)

# 最终队列中只剩已排序好的列表,pop出返回即可

return merge_queue.pop()

性能测试

用网上搜索得到的他人的循环实现的归并排序,命名为otherMergeSort(来自https://www.jianshu.com/p/bbba9b717f64

),自己写的递归实现的排序,队列循环实现的排序,快速排序进行简单的数量为百万级别的排序性能测试。结果如下。后面会贴出各排序代码及辅助函数的代码。

网上循环实现的归并排序用时 18.8429724 s

队列循环迭代归并排序用时 10.696805300000001 s

递归归并排序用时 10.5611496 s

快排用时 7.46601840000001 s

可以看出队列循环迭代实现的方法与递归排序实现的方法执行时间差不多,都比网上循环实现的方法好一点。然而前三个排序终归都还是归并排序,还是比不过快排呀。

循环实现与递归实现异同

在效率上是差不多的(不知道为什么别人说循环实现效率会高一点,在简单性能测试看来排序时间没多大的区别,或许还有优化的点我没找到?还是空间效率或其他问题呢?欢迎讨论)

在链表的归并排序上应该只能用循环实现的方法,因为链表无法快速取得中点来分得小数组。

优化杂思

在学习时有看到几个优化的点。递归实现的时候传入参数可以把数组切片变为起始点位置。我优化了之后用同款性能测试,性能并没有什么区别,并且代码更不易理解了。同样的,循环实现和递归实现的性能在上面的性能测试下也没什么区别。希望有大神来解一下惑。

最能优化的是在排序合并时用索引取出两列表排序的最小值来比较之后加入新列表,而不是直接pop(0)取出,因为列表pop(0)的时间复杂度是O(n),而用列表索引取是O(1)。这样优化后一百万数量的排序从60S变成10S。

性能测试用到的排序算法代码及辅助函数代码

简单性能测试代码

if __name__ == '__main__':

int_num = 1000000

last = int_num - 1

max = 1000000

try_num = 1

print('网上循环实现的归并排序用时', timeit.timeit("otherMergeSort({})".format(generateRandomArray(int_num, 0, max)),

setup="from __main__ import otherMergeSort",

number=try_num))

print('队列循环迭代归并排序用时', timeit.timeit("que_merge({})".format(generateRandomArray(int_num, 0, max)),

setup="from __main__ import que_merge",

number=try_num))

print('递归归并排序用时',timeit.timeit("my_emerge_sort({})".format(generateRandomArray(int_num, 0, max)),

setup="from __main__ import my_emerge_sort",

number=try_num))

print('快排用时',timeit.timeit("quick_sort({},{},{})".format(generateRandomArray(int_num, 0, int_num), 0, last),

setup="from __main__ import quick_sort",

number=try_num))

辅助函数代码

生成随机数组

def generateRandomArray(n, min, max):

arr = [randint(min, max,) for _ in range(n)]

return arr

网上循环实现的归并排序

def otherMergeSort(nums):

l = len(nums)

global nums_for_compare

nums_for_compare = list(range(l))

sz = 1

# sz = 1, 2, 4, 8

while sz < l:

# left = 0, 2, 4, 6

left = 0

while left < l - sz:

__merge_of_two_sorted_array(nums, left, left + sz - 1, min(left + sz + sz - 1, l - 1))

left += 2 * sz

sz *= 2

自己写的递归实现的归并排序

def my_emerge_sort(array):

length = len(array)

if length <= 1:

return array

left_list = my_emerge_sort(array[:length // 2])

right_list = my_emerge_sort(array[length // 2:length])

l, r = 0, 0

new_list = []

while l < len(left_list) and r < len(right_list):

if left_list[l] <= right_list[r]:

new_list.append(left_list[l])

l += 1

else:

new_list.append(right_list[r])

r += 1

new_list += left_list[l:]

new_list += right_list[r:]

return new_list

快速排序代码

def quick_sort(lists, i, j):

if i >= j:

return lists

pivot = lists[i]

low = i

high = j

while i < j:

while i < j and lists[j] >= pivot:

j -= 1

lists[i] = lists[j]

while i < j and lists[i] <= pivot:

i += 1

lists[j] = lists[i]

lists[j] = pivot

quick_sort(lists, low, i - 1)

quick_sort(lists, i + 1, high)

return lists

python递归合并排序_python 归并排序的递归法与迭代法(利用队列)实现,以及性能测试...相关推荐

  1. python递归排序_Python归并排序(递归实现)

    为什么归并排序如此有用? 1. 快捷和稳定 归并排序成为⼀一个非常棒的排序算法主要是因为它的快捷和稳 定.它的复杂度即使在最差情况下都是O(n log n).而快速排序 在最差情况下的复杂度是O(n^ ...

  2. C++非递归合并排序的通用实现算法(附完整源码)

    C++非递归合并排序的通用实现算法 C++非递归合并排序的通用实现算法完整源码(定义,实现,main函数测试) C++非递归合并排序的通用实现算法完整源码(定义,实现,main函数测试) #inclu ...

  3. [转载] 算法导论:分治法,python实现合并排序MERGE-SORT

    参考链接: Python中的合并排序merge sort 1. 简单合并排序法实现 思想:两堆已排好的牌,牌面朝下,首先掀开最上面的两张,比较大小取出较小的牌,然后再掀开取出较小牌的那一堆最上面的牌和 ...

  4. 递归函数python有什么特点_Python中的递归

    在前面的讲解中,函数的调用通常发生在彼此不同的函数之间.其实,函数还有一种特殊的调用方式,那就是自己调用自己,这种方式称为函数递归调用. 递归,在程序设计中也是一个常用的技巧,甚至是一种思维方式,非常 ...

  5. 合并排序(归并排序)

    合并排序,大致思想便是先将数组中的元素拆分成若干小部分,然后再将这些小部分按照顺序进行重新组合,从而实现排序.很明显,这里用到了分治法的思想,即将一个大问题分成若干个相同的小问题,因为问题规模变小了, ...

  6. python八大选择排序_Python实现选择排序

    选择排序: 选择排序(Selection sort)是一种简单直观的 排序算法 .它的工作原理如下.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最 ...

  7. python字母大小写排序_Python中sorted()排序与字母大小写的问题

    今天我在练习python时,对字典里的键用sorted排序时发现并没有按照预期排序 研究后发现字母大小写会影响排序 首先创建一个字典,键里面的首字母有大写有小写 favorite_digit = { ...

  8. python大文件排序_Python如何实现大文件排序?Python大文件排序的实现方法

    Python如何实现大文件排序?Python大文件排序的实现方法 本文实例讲述了Python实现大文件排序的方法.分享给大家供大家参考.具体实现方法如下: import gzip import os ...

  9. python简单选择排序_Python实现冒泡,插入,选择排序简单实例

    本文所述的Python实现冒泡,插入,选择排序简单实例比较适合Python初学者从基础开始学习数据结构和算法,示例简单易懂,具体代码如下: # -*- coding: cp936 -*-#python ...

最新文章

  1. JavaScript教程——函数(arguments 对象)
  2. 账号解锁_WOW正式服:周四新CD,解锁账号精华共享的正确姿势
  3. energy计算机电脑,energy management
  4. 16. 最接近的三数之和
  5. python leetcode_python实现leetcode中无重复字符的最长字串
  6. python supervisor flask_supervisor配合uwsgi部署flask应用
  7. SSH远程登录失败,提示“Password authentication failed”
  8. java中session的作用_java中session的工作原理是什么?和Cookies有何区别?
  9. er图转换成关系模型的例题,将ER图转换为关系模型
  10. html 隐藏input
  11. JAVA变量初始化赋值null
  12. ParticleSystem的使用
  13. Mysql两个引擎对比
  14. nginx 错误Failed to start The nginx HTTP and reverse proxy server.
  15. pyqt:使用qrc文件配置图片资源,生成py文件
  16. Nginx流媒体服务器搭建
  17. SQL Server 2008 R2每天自动备份数据库
  18. 计算机一级改扩展名,怎么改文件扩展名,教您电脑win7改文件扩展名的方法
  19. 浅析贴片电感的作用及使用原理
  20. matlab 如何使用虚数,编程高手帮我解决下怎么用matlab解含有虚数的微分方程组...

热门文章

  1. 知识星球作业(第5周) - 关于view的知识
  2. 麦肯锡:潜力巨大,障碍多多,人工智能医疗是蓝海还是虚火?
  3. 二分查找(递归与非递归)
  4. IMAP 称作交互邮件访问协议
  5. 小猫TOMCAT其实很可爱
  6. springboot+vue前后端分离实现宿舍管理系统
  7. 微服务限流Sentinel讲解(四)
  8. 深入浅出详细介绍Java异常,让你茅塞顿开般的感觉
  9. 零基础Java学习之初始化块
  10. Angualr6表单提交验证并跳转