排序
概述
- 以下所有代码可以找一个在线运行python的代码,直接运行
- 排序算法分内部排序与外部排序,内部排序是数据记录在内存中进行排序,外部排序为排序数较大而需访问外存。常见的内部排序算法有:
冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序、基数排序等
快排
思路
思路一:
针对一系列需要排序的数字,每一次都确定其中一个元素的真正位置,左边都比这个数字小,右边都比这个数字大。
不断迭代,确定出最后整体每个元素的最终位置。
需要两个游标,一个low 一个high,一般都是 不断确定第一个元素的位置
初始化状态,low指向待排序第一个元素,high指向待排序最后一个元素。
然后high往前移动,直到移动到比第一个元素即要确定位置的元素小的元素,此时把low元素指向的位置赋值为high指向的元素。
再然后,high不动了,low往前移动,直到移动到元素比带确定位置元素大的元素,此时把high元素指向的位置赋值为low指向的元素。
如此反复,直到high和low重合了,则第一次排序结束,即第一个需要确定最终位置的元素,它的位置找到了。
最后,把左右子集,不断重复上述过程
代码
思路一:
快排的代码中,就两个点很重要。 第一个点就是low游标和high游标交换,互相赋值的过程 第二个点就是不断递归,在子集合中求解待排序元素的最终位置
第一版代码
# python3版本 def quickSort(needList,first,last): if(first>last): return if first==last: return needList low=first # 注意点一 low和high游标和一开始传入的first last要用不同变量,以便处理下面的递归调用 high=last num1=needList[low] # 注意点二 要把每次需要排的元素单独拿出来 while low<high: while needList[high]>num1 and low<high: # 注意点三 移动 赋值 两个操作都要有 且这里也要写上low<high条件,否则会报错 这里和下面都试过写if的逻辑,结果不太好写 high-=1 needList[low]=needList[high] while needList[low]<=num1 and low<high: # 注意点四 在low游标和high游标,得选择一方拥有等号,以处理待排序之中,含有相等元素 low+=1 needList[high]=needList[low] needList[low]=num1 quickSort(needList,first,low-1) quickSort(needList,high+1,last) # 注意点六 两个递归,后半部分的递归开始要是High+1,这个跟你一开始选择的是排序哪一个元素有关,如果是排序第一个元素,那就是这样high+1 return needList print(quickSort([6,3,5],0,2))
时空复杂度和应用场景
思路一:
使用场景:当初始序列无序的时候,越有序,该算法的性能相对越差,因为如果整个序列是有序的,那么本来可以每次少比一半的情况就没有了,会变成每次都要比全部元素,则性能就会差
时空复杂度都是针对python代码来分析的:
时空复杂度:最优情况:即每次确定的元素都恰巧平分需要排序的列表,此时的时间复杂度是 n l o g n nlogn nlogn,原因是这个时候递归处的执行次数是 l o g n logn logn,然后每次递归函数内部的最深层循环都是n,就是while循环那里。所以总的时间复杂度为 n l o g n nlogn nlogn。
最坏情况:即每次确定的元素都是在原位置没动,即原列表是有序的。此时的时间复杂度是 n 2 n^2 n2,原因是这个时候递归处的执行次数是n,然后每次递归函数内部的最深层循环都是n,就是while循环那里。所以总的时间复杂度为 n 2 n^2 n2。
平均情况: 涉及到较为复杂的推导,可以先记下。 n l o g n nlogn nlogn
从实际运行效果上来:在最优情况下,总的运行次数是 n l o g n + n l o g n nlogn+nlogn nlogn+nlogn,但是最坏情况下,总的运行次数是 n 2 n^2 n2,所以存在最优和最差情况,不要只盯着第一层看,要看总的运行次数和时间
空间复杂度:还是递归的地方占用了空间, 最 优 l o g n , 最 坏 n 最优logn,最坏n 最优logn,最坏n
冒泡
思路
思路一:
对于待排序元素,首先确定排序规则,是由大到小,还是由小到大,然后首先确定首元素还是尾元素
下面只介绍最优的算法,使用最少的比较次数,且假设按照从小到大的顺序,首先确定首元素
从后到前,每相邻两个元素相互比较,依次往前迭代,当后比前小的时候,就交换两者顺序
此处,如果首先要确定是尾元素,那就从前到后,每相邻两个元素进行比较
按照上面那种方法,每次到最前面都是最小的,第一个优化点,就是已经排好序的元素不用再参与排序,只比较到前一个即可
第二个优化点,就是按照3中的方法,一旦某趟比较没有发生交换,说明整体都已经有序,就无需在进行后续比较
代码
思路一:
第一版代码
# python递归模式 def maoPaoSort(num:list,final:int)->list: if final<0 or len(num)==0: return if len(num)==1: return num changeNum=0 for i in range(final): if num[i]>num[i+1]: tmp=num[i] num[i]=num[i+1] num[i+1]=tmp changeNum+=1 if changeNum==0: # 针对上面所说的第二个优化点 return num else: maoPaoSort(num,final-1) return num maoPaoSort([6,3,5],2)第二版代码
# python的非递归模式 从前往后冒泡 def maoPaoSort(num:list)->list: length=len(num) if length==0: return if length==1: return num i,changeNum=0,0 while length>1: while i<length-1: if num[i]>num[i+1]: tmp=num[i] num[i]=num[i+1] num[i+1]=tmp changeNum+=1 else: i+=1 if changeNum==0: return num length-=1 return num maoPaoSort([6,3,5]) #python的非递归模式 从后往前冒泡 def buddleSort(listExp): if not len(listExp): # 注意点二 if not 0 是返回true的 return for i in range(len(listExp)): j=len(listExp)-1 changeNum=0 # 注意点一 用来记录每趟比较过程中,发生交换的次数 对应优化点二 while j>i: if(listExp[j]<listExp[j-1]): listExp[j],listExp[j-1]=listExp[j-1],listExp[j] changeNum+=1 j-=1 if(changeNum==0): return listExp print(buddleSort([6,3,5]))
时空复杂度&应用场景
思路一:
使用场景:1. 顺序存储 2.原表越有序,性能相对越好
时间复杂度:
最 坏 o ( n 2 ) 最 好 o ( n ) 平 均 O ( n 2 ) 最坏 o(n^2) 最好 o(n) 平均O(n^2) 最坏o(n2)最好o(n)平均O(n2)
最坏在递归那个方法的推导:
- 每递归一次,需要执行的次数分别为n-1,n-2,…直到1 所以,时间复杂度是 [ ( n − 1 ) + 1 ] ∗ ( n − 1 ) 2 − − − − > O ( n 2 ) \frac{[(n-1)+1]*(n-1)}{2}---->O(n^2) 2[(n−1)+1]∗(n−1)−−−−>O(n2)
空间复杂度:
- 第一版:最好o(1) 最坏: o ( n ) o(n) o(n),就是走了递归,递归是要消耗堆栈的
- 第二版:O(1) 没有特定需要存储空间的东西
6万+

被折叠的 条评论
为什么被折叠?



