所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。
评价标准
- 时间复杂度:即从序列的初始状态到经过排序算法的变换移位等操作变到最终排序好的结果状态的过程所花费的时间度量。
- 稳定性:描述算法对原始序列处理前后,该序列相等大小的元素前后位置是否发生改变
- 空间复杂度:就是从序列的初始状态经过排序移位变换的过程一直到最终的状态所花费的空间开销。
常用排序算法
1.冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
算法描述
-
比较相邻的元素。如果第一个比第二个大,就交换它们两个;
-
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
-
针对所有的元素重复以上的步骤,除了最后一个;
-
重复步骤1~3,直到排序完成。
动图演示
视频讲解
https://www.bilibili.com/video/av18176281
代码实现
def bubble_sort(arr):
Len = len(arr)
for i in range(Len-1):
for j in range(Len-i-1):
if arr[j] > arr[j+1]:
arr[j],arr[j+1] = arr[j+1],arr[j]
return arr
2.选择排序
选择排序提高了冒泡排序的性能,它每遍历一次列表只交换一次数据,即进行一次遍历时找 到最大的项,完成遍历后,再把它换到正确的位置。和冒泡排序一样,第一次遍历后,最大的数 据项就已归位,第二次遍历使次大项归位。这个过程持续进行,一共需要 n-1 次遍历来排好 n 个数 据,因为最后一个数据必须在第 n-1 次遍历之后才能归位。
算法描述
-
在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
-
从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
-
重复第二步,直到所有元素均排序完毕。
动图演示
视频讲解
https://www.bilibili.com/video/av18176082
代码实现
def selection_sort(arr):
Len = len(arr)
for i in range(Len-1):
min_index = i
for j in range(i+1,Len):
if arr[min_index] > arr[j]:
arr[j],arr[min_index] = arr[min_index],arr[j]
return arr
3. 插入排序
3.1直接插入排序
插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
算法描述
-
把待排序的数组分成已排序和未排序两部分,初始的时候把第一个元素认为是已排好序的。
-
从第二个元素开始,在已排好序的子数组中寻找到该元素合适的位置并插入该位置。
-
重复上述过程直到最后一个元素被插入有序子数组中。
动图演示
视频讲解
https://www.bilibili.com/video/av18980488
代码实现
def insertion_sort(arr):
Len = len(arr)
for i in range(1,Len):
for j in range(i,0,-1):
if arr[j] < arr[j-1]:
arr[j],arr[j-1] = arr[j-1],arr[j]
return arr
3.2 希尔排序
算法描述
-
选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
-
按增量序列个数k,对序列进行 k 趟排序;
-
每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
动图演示
视频讲解
https://www.bilibili.com/video/av15961896?from=search&seid=3767742559497289963
代码实现
def shell_sort(arr):
step = len(arr)//2
while step > 0:
for i in range(step,len(arr)):
# 类似插入排序, 当前值与指定步长之前的值比较, 符合条件则交换位置
while i >= step and arr[i-step] > arr[i]:
arr[i], arr[i-step] = arr[i-step], arr[i]
i -= step
step = step//2
return arr
4. 快速排序
算法描述
-
从数列中挑出一个元素,称为"基准"(pivot),
-
重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
-
递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。
动图演示
视频讲解
https://www.bilibili.com/video/av18980345
代码实现
def quick_sort(nums):
if len(nums) <= 1:
return nums
# 随意选取一个基准数,比如选取列表第一个数
base = nums[0]
# left列表为nums中比基准数base小或等于base的数组成的列表
left = [x for x in nums[1:] if x <= base]
# right列表为nums中比基准数base大的数组成的列表
right = [x for x in nums[1:] if x > base]
# 对left和right列表递归排序
return quick_sort(left) + [base] + quick_sort(right)
5. 堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆排序就是把最大堆堆顶的最大数取出,将剩余的堆继续调整为最大堆,再次将堆顶的最大数取出,这个过程持续到剩余数只有一个时结束。
算法描述
-
将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
-
将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
-
由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
动图演示
视频讲解
https://www.bilibili.com/video/av18980178
代码实现
def HeapSort(ls):
def heapadjust(arr,start,end): #将以start为根节点的堆调整为大顶堆
temp=arr[start]
son=2*start+1
while son<=end:
if son<end and arr[son]<arr[son+1]: #找出左右孩子节点较大的
son+=1
if temp>=arr[son]: #判断是否为大顶堆
break
arr[start]=arr[son] #子节点上移
start=son #继续向下比较
son=2*son+1
arr[start]=temp #将原堆顶插入正确位置
#######
n=len(ls)
if n<=1:
return ls
#建立大顶堆
root=n//2-1 #最后一个非叶节点(完全二叉树中)
while(root>=0):
heapadjust(ls,root,n-1)
root-=1
#掐掉堆顶后调整堆
i=n-1
while(i>=0):
(ls[0],ls[i])=(ls[i],ls[0]) #将大顶堆堆顶数放到最后
heapadjust(ls,0,i-1) #调整剩余数组成的堆
i-=1
return ls
arr = [3,2,4,1,6]
HeapSort(arr)
6. 归并排序
算法描述
- 将序列每相邻两个数字进行归并操作,形成ceil(n/2)个序列,排序后每个序列包含两/一个元素
- 若此时序列数不是1个则将上述序列再次归并,形成ceil(n/4)个序列,每个序列包含四/三个元素
- 重复步骤2,直到所有元素排序完毕,即序列数为1
图片演示
视频讲解
https://www.bilibili.com/video/av18980253
代码实现
def MergerSort(lists):
if len(lists)<=1:
return lists
num=int(len(lists)/2)
left=MergerSort(lists[:num])
right=MergerSort(lists[num:])
return Merge(left,right)
def Merge(left,right):
r,l=0,0
result=[]
while l<len(left) and r<len(right):
if left[l]<=right[r]:
result.append(left[l])
l+=1
else:
result.append(right[r])
r+=1
result+=list(left[l:])
result+=list(right[r:])
return result
print(MergerSort([22,3,2,8,65,8,9,1,4,3]))
7. 桶排序
桶排序又叫箱排序,是计数排序的升级版,它的工作原理是将数组分到有限数量的桶子里,然后对每个桶子再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后将各个桶中的数据有序的合并起来。
算法描述
-
找出待排序数组中的最大值max、最小值min
-
我们使用 动态数组ArrayList 作为桶,桶里放的元素也用 ArrayList 存储。桶的数量为(max-min)/arr.length+1
-
遍历数组 arr,计算每个元素 arr[i] 放的桶
-
每个桶各自排序
-
遍历桶数组,把排序好的元素放进输出数组
动图演示
视频讲解
https://www.bilibili.com/video/av17940595
代码实现
def bucket_sort(nums):
max_num = max(nums)
bucket = [0]*(max_num + 1)
for i in nums:
bucket[i] += 1
sort_nums = []
for i in range(len(bucket)):
if bucket[i] != 0:
for j in range(bucket[i]):
sort_nums.append(i)
return sort_nums
nums = [3,2,4,1,6]
bucket_sort(nums)
8. 基数排序
基数排序(Radix Sort)是桶排序的扩展,它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。
排序过程:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
算法描述
-
取得数组中的最大数,并取得位数;
-
arr为原始数组,从最低位开始取每个位组成radix数组;
-
对radix进行计数排序(利用计数排序适用于小范围数的特点);
动图演示
视频讲解
https://www.bilibili.com/video/av18980653
代码实现
from random import randint
#利用列表的下标从小到大实现了排序
def RadixSort(list,d):
for k in range(d):#d轮排序
s=[[] for i in range(10)]#因为每一位数字都是0~9,故建立10个桶
'''对于数组中的元素,首先按照最低有效数字进行
排序,然后由低位向高位进行。'''
for i in list:
m=int(i/(10**k)%10)
s[m].append(i) # 977/(10**0)%10=7,s[7].append(977) ;977/10=97(小数舍去),87/100=0
#此处利用了S的下标的大小顺序来排列,正好关键字的大小
list=[j for i in s for j in i]#利用了for i in s,执行时会按s中下标从0开始
return list
if __name__ == '__main__':
a=[randint(1,999) for i in range(10)]#最多是三位数,因此d=3
print(a)
a=RadixSort(a,3)#将排好序的数组再赋给a!!!!
print(a)