1.选择排序:
选择法思路:先取一个元素进行与其后面的元素对比,如果后面的数据与这个数据的规律不符合,则记录这个数据的位置,继续对比,直到数据完毕,交换数据。
def select_sort(alist):
for i in range(len(alist)-1):
for j in range(i+1,len(alist)):
if alist[i]>alist[j]:
alist[i],alist[j]=alist[j],alist[i]
alist=[23,4,3,5,67,32,7,24]
select_sort(alist)
print(alist)
2.冒泡排序
冒泡排序原理: 每一趟只能将一个数归位, 如果有n个数进行排序,只需将n-1个数归位, 也就是说要进行n-1趟操作(已经归位的数不用再比较)。
def bubble_sort(alist):
# 遍历所有数组元素
for i in range(len(alist)):
for j in range(0, len(alist) - i - 1):
if alist[j]>alist[j+1]:
alist[j],alist[j+1]=alist[j+1],alist[j]
alist=[23,4,3,5,67,32,7,24]
bubble_sort(alist)
print(alist)
3.插入排序
具体实现步骤为:首先我们把整个数组拆分为有序区间和未排序区间,有序区间在插入排序一开始只有一个元素,就是数组的第一个元素。接在有序区间之后的一个元素就是准备插入的元素,在图中就是标为绿色的元素,在有序区间内寻找位置并插入。其寻找逻辑为:从后往前依次进行比较,如果待插入元素大于当前元素,则将待插入元素插入到当前元素的后一位,如果待插入元素小于当前元素,则将当前元素后移一位。不断重复该过程直至到数组的最后一位。
def insertion_sort(alist):
# 遍历所有数组元素
for i in range(1,len(alist)):
key=alist[i]
j=i-1
while j>=0:
if alist[j] >key:
alist[j+1] = alist[j]
j-=1
else:
break
alist[j+1]=key
i+=1
alist=[23,4,3,5,67,32,7,24]
insertion_sort(alist)
print(alist)
4.快速排序:
思想:先从待排序的数组中找出一个数作为基准数,然后将原来的数组划分成两部分:小于基准数的左子数组和大于等于基准数的右子数组。然后对这两个子数组再递归重复上述过程,直到两个子数组的所有数都分别有序。最后返回“左子数组” + “基准数” + “右子数组”,即是最终排序好的数组。
def quick_sort(alist, start, end):
if start >= end:
return
mid = alist[start]
left = start
right = end
# left与right未重合,就向中间移动
while left < right:
while left < right and alist[right] >= mid:
right -= 1
alist[left] = alist[right]
while left < right and alist[left] < mid:
left += 1
alist[right] = alist[left]
# 从循环退出后,left与right相遇,即left==right
alist[left] = mid
# 对左边部分执行快速排序
quick_sort(alist, start, left-1)
# 对右边部分执行快速排序
quick_sort(alist, left+1, end)
alist = [23,4,3,5,67,32,7,24]
quick_sort(alist, 0, len(alist) - 1)
print(alist)
【注】:
1.快速排序算法的平均时间复杂度为O(nlogn),通常认为在所有同数量级的排序算法中,快速排序的平均性能是最好的,这也是它被称为“快速排序”的原因。
2.快速排序算法相比于其他排序算法来说比较耗费空间资源,因为快速排序需要栈空间来实现递归。
3.快速排序的基准元素的选取非常重要,如果基准元素选取不当,可能影响排序过程的时间复杂度和空间复杂度。为了避免快速排序退化为冒泡排序以及递归栈过深等问题,通常依照“三者取中”的法则来选取基准元素。三者取中法是指在当前待排序的子序列中,将其首元素、尾元素和中间元素进行比较,在三者中取中值作为本趟排序的基准元素。
5.归并排序:
思想:先递归分解数组,再合并数组
原理:将数组分解最小之后,然后合并两个有序数组,基本思想是比较两个数组的最前面的数,谁小就取谁,取完后,将相应的指针后移以为。然后再比较,直到一个数组为空,最后把另一个数组的剩余部分复制过来即可。
def merge_sort(alist):
if len(alist) <= 1:
return alist
# 二分分解
num = len(alist) // 2
left = merge_sort(alist[:num])
right = merge_sort(alist[num:])
# 合并
return merge(left, right)
def merge(left, right):
'''合并操作,将两个有序数组left[]和right[]合并成一个大的有序数组'''
# left与right的下标指针
l, r = 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 += left[l:]
result += right[r:]
return result
alist = [23,4,3,5,67,32,7,24]
sorted_alist = merge_sort(alist)
print(sorted_alist)
6.希尔排序:
def shellSort(alist):
# 设定步长
step = len(alist)//2
while step > 0:
for i in range(step, len(alist)):
# 类似插入排序, 当前值与指定步长之前的值比较, 符合条件则交换位置
while i >= step and alist[i-step] > alist[i]:
alist[i], alist[i-step] = alist[i-step], alist[i]
i -= step
step = step//2
return alist
alist = [23,4,3,5,67,32,7,24]
shellSort(alist)
print(shellSort(alist))
7.堆排序
思想:堆排序是简单选择排序的改进算法,简单选择排序在待排序的个数据中选择一个最小的元素需要进行n-1次的比较,但是并没有将每一次循环的结果保存下来,在下一次循环中,有很多比较已经在上一次的循环中做过了,但由于上一次循环时没有保存这些比较结果,所以下一次循环时又要重复这些比较操作,因此数据的比较次数较多。堆排序可以做到每次在选择最小记录的同时,根据比较结果对其他元素做出相应的调整。
堆的特点就是FIFO(first in first out)先进先出,这里的话我觉得可以理解成树的结构。
堆在接收数据的时候先接收的数据会被先弹出。
栈的特性正好与堆相反,是属于FILO(first in/last out)先进后出的类型。
栈处于一级缓存而堆处于二级缓存中。这个不是本文重点所以不做过多展开。
堆(定义):(二叉)堆数据结构是一个数组对象,可以视为一棵完全二叉树。如果根结点的值大于(小于)其它所有结点,并且它的左右子树也满足这样的性质,那么这个堆就是大(小)根堆。
def build_heap(alist):
"""建立一个堆"""
# 自底向上建堆
for i in range(len(alist) //2 - 1, -1, -1):
heap(alist, len(alist), i)
def heap(alist, heap_size, index):
"""调整列表中的元素以保证以index为根的堆是一个最大堆"""
# 将当前结点与其左右子节点比较,将较大的结点与当前结点交换,然后递归地调整子树
left_child = 2 * index + 1
right_child = left_child + 1
if left_child < heap_size and alist[left_child] > alist[index]:
largest = left_child
else:
largest = index
if right_child < heap_size and alist[right_child] > alist[largest]:
largest = right_child
if largest != index:
alist[index], alist[largest] = alist[largest], alist[index]
heap(alist, heap_size, largest)
def heapsort(alist):
"""堆排序"""
# 先将列表调整为堆
build_heap(alist)
heap_size = len(alist)
# 调整后列表的第一个元素就是这个列表中最大的元素,将其与最后一个元素交换,然后将剩余的列表再调整为最大堆
for i in range(len(alist) - 1, 0, -1):
alist[i], alist[0] = alist[0], alist[i]
heap_size -= 1
heap(alist, heap_size, 0)
if __name__ == '__main__':
alist = [23, 4, 3, 5, 67, 32, 7, 24]
heapsort(alist)
print(alist)
8.基数排序
基数排序思想:基数排序的实现,是将原数组中的数值,分别按照个位,十位,百位......等的数值依次分组,比如,先按照个位数值分组,将原数组的元素按照个位数字进行依次分配,然后将分配好的元素,依次按照个位数字的大小依次存入到原数组中,然后再将该数组按照十位,百位....上的数字分组,最终实现排序。
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
def radix_sort(alist):
"""基数排序"""
i = 0 # 记录当前正在排拿一位,最低位为1
max_num = max(alist) # 最大值
j = len(str(max_num)) # 记录最大值的位数
while i < j:
bucket_list =[[] for _ in range(10)] #初始化桶数组
for x in alist:
bucket_list[int(x / (10**i)) % 10].append(x) # 找到位置放入桶数组
alist.clear()
for x in bucket_list: # 放回原序列
for y in x:
alist.append(y)
i += 1
if __name__ == '__main__':
alist = [23,4,3,5,67,32,7,24]
radix_sort(alist)
print(alist)