1、常用排序算法比较与分析一、常用排序算法简述下面主要从排序算法的基本概念、原理出发,分别从算法的时间复杂度、空间复杂度、算法的稳定性和速度等方面进行分析比较。依据待排序的问题大小(记录数量 n)的不同,排序过程中需要的存储器空间也不同,由此将排序算法分为两大类:【内排序】、【外排序】。内排序:指排序时数据元素全部存放在计算机的随机存储器 RAM 中。外排序:待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中还需要对外存进行访问的排序过程。先了解一下常见排序算法的分类关系(见图 1-1)图 1-1 常见排序算法二、内排序相关算法2.1 插入排序核心思想:将一个待排序的数据元素插入到前
2、面已经排好序的数列中的适当位置,使数据元素依然有序,直到待排序数据元素全部插入完为止。2.1.1 直接插入排序核心思想:将欲插入的第 i 个数据元素的关键码与前面已经排序好的 i-1、i-2 、i-3、 数据元素的值进行顺序比较,通过这种线性搜索的方法找到第 i 个数据元素的插入位置 ,并且原来位置 的数据元素顺序后移,直到全部排好顺序。直接插入排序中,关键词相同的数据元素将保持原有位置不变,所以该算法是稳定的,时间复杂度的最坏值为平方阶 O(n2),空间复杂度为常数阶 O(l)。Python 源代码:1. #-直接插入排序- 2. def insert_sort(data_list): 3.
3、 #遍历数组中的所有元素,其中 0 号索引元素默认已排序,因此从 1 开始 4. for x in range(1, len(data_list): 5. #将该元素与已排序好的前序数组依次比较,如果该元素小,则交换 6. #range(x-1,-1,-1):从 x-1 倒序循环到 0 7. for i in range(x-1, -1, -1): 8. #判断:如果符合条件则交换 9. if data_listi data_listi+1: 10. temp = data_listi+1 11. data_listi+1 = data_listi 12. data_listi = temp
4、2.1.2 希尔排序核心思想:是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1时,整个文件恰被分成一组,算法便终止。希尔排序时间复杂度会比 O(n2)好一些,然而,多次插入排序中,第一次插入排序是稳定的,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,所以希尔排序是不稳定的。Python 源代码:1. #-希尔排序- 2. def insert_shell(data_list): 3. #初始化 step 值,此处利用序列长度的一半为其赋值 4. group = int(len(data_list)/2)
5、5. #第一层循环:依次改变 group 值对列表进行分组 6. while group 0: 7. #下面:利用直接插入排序的思想对分组数据进行排序 8. #range(group,len(data_list):从 group 开始 9. for i in range(group, len(data_list): 10. #range(x-group,-1,-group):从 x-group 开始与选定元素开始倒序比较,每个比较元素之间间隔 group 11. for j in range(i-group, -1, -group): 12. #如果该组当中两个元素满足交换条件,则进行交换 1
6、3. if data_listj data_listj+group: 14. temp = data_listj+group 15. data_listj+group = data_listj 16. data_listj = temp 17. #while 循环条件折半 18. group = int(group / 2) 2.2 选择排序核心思想:每一趟扫描时,从待排序的数据元素中选出关键码最小或最大的一个元素,顺序放在已经排好顺序序列的最后,直到全部待排序的数据元素排完为止。2.2.1 直接选择排序核心思想:给每个位置选择关键码最小的数据元素,即:选择最小的元素与第一个位置的元素交换,然
7、后在剩下的元素中再选择最小的与第二个位置的元素交换,直到倒数第二个元素和最后一个元素比较为止。根据其基本思想,每当扫描一趟时,如果当前元素比一个元素小,而且这个小元素又出现在一个和当前元素相等的元素后面,则它们的位置发生了交换,所以直接选择排序时不稳定的,其时间复杂度为平方阶 O(n2),空间复杂度为 O(l)。Python 源代码:1. #-直接选择排序- 2. def select_sort(data_list): 3. #依次遍历序列中的每一个元素 4. for i in range(0, len(data_list): 5. #将当前位置的元素定义此轮循环当中的最小值 6. minim
8、um = data_listi 7. #将该元素与剩下的元素依次比较寻找最小元素 8. for j in range(i+1, len(data_list): 9. if data_listj data_listi): 20. largest = left 21. #print(左叶子节点 ) 22. else: 23. largest = i 24. #当右叶子节点的下标小于序列长度 并且 右叶子节点的值大于父节点时,将右叶子节点的下标值赋值给 largest 25. if (right data_listlargest): 26. largest = right 27. #print(右叶
9、子节点 ) 28. #如果 largest 不等于 i 说明当前的父节点不是最大值,需要交换值 29. if (largest != i): 30. temp = data_listi 31. data_listi = data_listlargest 32. data_listlargest = temp 33. i = largest 34. #print(largest) 35. continue 36. else: 37. break 38. 39. #* 建立大顶堆 * 40. def build_max_heap(data_list): 41. length = len(data_
10、list) 42. for x in range(int(length-1)/2),-1,-1): 43. adjust_max_heap(data_list,length,x) 44. 45. #* 堆排序 * 46. def heap_sort(data_list): 47. #先建立大顶堆,保证最大值位于根节点;并且父节点的值大于叶子结点 48. build_max_heap(data_list) 49. #i:当前堆中序列的长度.初始化为序列的长度 50. i = len(data_list) 51. #执行循环:1. 每次取出堆顶元素置于序列的最后(len-1,len-2,len-3
11、.) 52. # 2. 调整堆,使其继续满足大顶堆的性质,注意实时修改堆中序列的长度 53. while i 0: 54. temp = data_listi-1 55. data_listi-1 = data_list0 56. data_list0 = temp 57. #堆中序列长度减 1 58. i = i-1 59. #调整大顶堆 60. adjust_max_heap(data_list, i, 0) 2.3 交换排序核心思想:顾名思义,就是一组待排序的数据元素中,按照位置的先后顺序相互比较各自的关键码,如果是逆序,则交换这两个数据元素,直到该序列数据元素有序为止。2.3.1 冒泡
12、排序核心思想:对于待排序的一组数据元素,把每个数据元素看作有重量的气泡,按照轻气泡不能在重气泡之下的原则,将未排好顺序的全部元素自上而下的对相邻两个元素依次进行比较和调整,让较重的元素往下沉,较轻的往上冒。根据基本思想,只有在两个元素的顺序与排序要求相反时才将调换它们的位置,否则保持不变,所以冒泡排序时稳定的。时间复杂度为平方阶O(n2),空间复杂度为 O(l)。Python 源代码:1. #-冒泡排序- 2. def bubble_sort(data_list): 3. length = len(data_list) 4. #序列长度为 length,需要执行 length-1 轮交换 5.
13、 for x in range(1,length): 6. #对于每一轮交换,都将序列当中的左右元素进行比较 7. #每轮交换当中,由于序列最后的元素一定是最大的,因此每轮循环到序列未排序的位置即可 8. for i in range(0,length-x): 9. if data_listi data_listi+1: 10. temp = data_listi 11. data_listi = data_listi+1 12. data_listi+1 = temp 2.3.2 快速排序快速排序是对冒泡排序本质上的改进。核心思想:是一个就地排序,分而治之,大规模递归的算法。即:通过一趟扫描
14、后确保基准点的这个数据元素的左边元素都比它小、右边元素都比它大,接着又以递归方法处理左右两边的元素,直到基准点的左右只有一个元素为止。快速排序时一个不稳定的算法,其最坏值的时间复杂度为平方阶 O(n2),空间复杂度为 O(log2n)。Python 源代码:1. #-快速排序- 2. #data_list:待排序的序列;start 排序的开始 index,end 序列末尾的 index 3. #对于长度为 length 的序列:start = 0;end = length-1 4. def quick_sort(data_list,start,end): 5. if start = pivot
15、): 10. j = j-1 11. #将小于 pivot 的值移到左边 12. if (i 0): 13. maxNum = (int)(maxNum/10) 14. times = times+1 15. return times 16. #找到 num 从低到高第 pos 位的数据 17. def get_num_pos(num,pos): 18. return (int)(num/(10*(pos-1)%10 19. #基数排序 20. def radix_sort(data_list): 21. count = 10*None #存放各个桶的数据统计个数 22. bucket = l
16、en(data_list)*None #暂时存放排序结果 23. #从低位到高位依次执行循环 24. for pos in range(1,radix_sort_nums(data_list)+1): 25. #置空各个桶的数据统计 26. for x in range(0,10): 27. countx = 0 28. #统计当前该位( 个位,十位,百位)的元素数目 29. for x in range(0,len(data_list): 30. #统计各个桶将要装进去的元素个数 31. j = get_num_pos(int(data_listx),pos) 32. countj = co
17、untj+1 33. #counti表示第 i 个桶的右边界索引 34. for x in range(1,10): 35. countx = countx + countx-1 36. #将数据依次装入桶中 37. for x in range(len(data_list)-1,-1,-1): 38. #求出元素第 K 位的数字 39. j = get_num_pos(data_listx,pos) 40. #放入对应的桶中,countj-1 是第 j 个桶的右边界索引 41. bucketcountj-1 = data_listx 42. #对应桶的装入数据索引-1 43. countj
18、= countj-1 44. # 将已分配好的桶中数据再倒出来,此时已是对应当前位数有序的表 45. for x in range(0,len(data_list): 46. data_listx = bucketx 三、排序算法实测图 3-1 常用排序算法测试统计四、排序算法对比与分析表 4-1 各个排序算法比较直接插入排序 是对冒泡排序的改进,比冒泡排序快,但是只适用于数据量较小(1000 ) 的排序希尔排序比较简单,适用于小数据量 (5000 以下 )的排序,比直接插入排序快、冒泡排序快,因此,希尔排序适用于小数据量的、排序速度要求不高的排序。直接选择排序 和冒泡排序算法一样,适用于 n
19、 值较小的场合,而且是排序算法发展的初级阶段,在实际应用中采用的几率较小。堆排序比较适用于数据量达到百万及其以上的排序,在这种情况下,使用递归设计的快速排序和归并排序可能会发生堆栈溢出的现象。冒泡排序是最慢的排序算法,是排序算法发展的初级阶段,实际应用中采用该算法的几率比较小。快速排序是递归的、速度最快的排序算法,但是在内存有限的情况下不是一个好的选择;而且,对于基本有序的数据序列排序,快速排序反而变得比较慢。归并排序比堆排序要快,但是需要的存储空间增加一倍。基数排序适用于规模 n 值很大的场合,但是只适用于整数的排序,如果对浮点数进行基数排序,则必须明确浮点数的存储格式,然后通过某种方式将其映射到整数上,最后再映射回去,过程复杂。【编辑推荐】1. Python 分布式抓取和分析京东商城评价2. 硅谷资深数据科学家教你认清探索性数据分析(EDA )的价值3. 像 Excel 一样使用 python 进行数据分析 -(2)4. 数据和分析带来五大积极业务成果5. 文本分析之制作网络关系图 Python