1、冒泡排序(bubble sort)
冒泡排序每次比较相邻的元素,如果前面一个数大,则交换,否则,不交换。
首先,定义了一个随机数列,然后是冒泡排序,时间复杂度O(n^2)
import random
import random
def get_list(num):
list_ = []
for i in range(num):
list_.append(random.randint(0,num))
return list_
def bubble_sort(list1):
for i in range(len(list1)-1,0,-1):
for j in range(i):
if list1[j]>list1[j+1]:
list1[j],list1[j+1]=list1[j+1],list1[j]
return list1
list1 = get_list(10)
print(list1)
list2 = bubble_sort(list1)
print(list2)
结果:
[5, 5, 0, 4, 8, 5, 4, 6, 6, 1]
[0, 1, 4, 4, 5, 5, 5, 6, 6, 8]
每次运行的结果都不一样,但都是从小到大排序。
如果在排序的过程中已经有序,可以提前比较
def bubble_sort(arr):
length=len(arr)
if length<2:
return arr
else:
for i in range(length-1):
flag=False
for j in range(length -1 -i):
if arr[j]>arr[j+1]:
arr[j],arr[j+1] = arr[j+1],arr[j]
flag=True
if not flag:
break
return arr
双向排序-鸡尾酒排序
def cocktail_sort(arr):
length = len(arr)
for i in range(length - 1):
flag = False
for j in range(length -1 -i):
if arr[j]>arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
flag = True
if flag:
flag=False
for k in range(length -2 -i, 0, -1):
if arr[k]<arr[k-1]:
arr[k], arr[k-1] = arr[k-1],arr[k]
flag = True
if not flag:
break
return arr
2、选择排序
选择排序,选择最小的元素放在前,再从后面n-1个数,选择最小的,放在第二个位置,一直类推。。。
import random
def get_list(num):
list_ = []
for i in range(num):
list_.append(random.randint(0,num))
return list_
def selection_sort(list1):
length = len(list1)
for i in range(length-1):
min_index = i
for j in range(i+1,length):
if list1[min_index] > list1[j]:
min_index = j
if min_index !=i:
list1[i],list1[min_index] = list1[min_index],list1[i]
return list1
list1 = get_list(10)
print(list1)
list2 = selection_sort(list1)
print(list2)
结果:
[6, 10, 3, 1, 6, 7, 5, 4, 10, 9]
[1, 3, 4, 5, 6, 6, 7, 9, 10, 10]
3、插入排序
插入排序,是待排序的元素,插入前面已经排序好的序列。
先把序列分为两部分,一是已经排序好的,二是待排序的混乱排序。
有两个序列,也就需要用for来遍历两个序列。
对于从小到大的升序排序,从未排序的序列中拿出一个元素,遍历已排序好的元素,比较大小后,确定其位置。
由于是未排序的元素插入到已经排序好的序列中,就做插入排序。
import random
import random
def get_list(num):
list_ = []
for i in range(num):
list_.append(random.randint(0,num))
return list_
def insertion_sort(list1):
length = len(list1)
for i in range(1,length):
for j in range(i,0,-1):
if list1[j] < list1[j-1]:
list1[j-1],list1[j] = list1[j],list1[j-1]
return list1
list1 = get_list(10)
print(list1)
list2 = insertion_sort(list1)
print(list2)
结果:
[3, 10, 2, 7, 10, 9, 5, 5, 10, 2]
[2, 2, 3, 5, 5, 7, 9, 10, 10, 10]
4,归并排序
归并排序,把序列分来,n是序列长度,n/2,n/4,n/8。。。一直分开到单个元素
然后是合并,合并前要排序
详细介绍归并排序,请看这个链接归并排序,博主写的很好
分开和合并的思路如下:
主要利用递归的思想,分开是递的过程,归是合并的过程。递归不好理解,要看成两部分,一个是递,一个是归。
递是逐渐展开,有一种从近到远的感觉;归是展开后的回归,有一种从远到近的感觉。
有一个数列,[4,5,2,1,9],分解过程如下:
4 5 2 1 9 5个数,5//2=2,两个分支,左分支2个元素,右分支剩下的元素
||
4 5 2 1 9 左边2个元素,分开。右边3个元素,3//2=1,左边一个元素 2,右边分支两个元素1 9
|| ||
4 5 2 1 9
||
1 9
然后是合并的过程:
比较两分支的第一个元素,拿出最小的
分开的两个元素1 , 9 合并,1,9 比较,结果【1,9】
2 1 9 的分支的合并,两个分支的第一个元素2 ,1 比较,拿出较小的1,然后2,9比较,拿出2,最后是9,结果【1,2,9】
4,5比较,结果【4,5】
【4,5】,【1,2,9】,同样两个分支的第一个元素比较拿出较小的,结果【1,2,4,5,9】
归并是先递归再合并
递归是二分法划分数据到树的子节点
合并是对两个有序序列的排序。
merge(A,B)是对两个有序序列的排序,从小到大排序
def merge(A,B):
result = []
left=right=0
while left<len(A) and right<len(B):
if A[left]<B[right]:
result.append(A[left])
left +=1
else:
result.append(B[right])
right +=1
if left == len(A):
result.extend(B[right:])
else:
result.extend(A[left:])
return result
def merge_sort(lis):
mid = len(lis)//2
if len(lis)==1:
return lis
left = merge_sort(lis[:mid])
right = merge_sort(lis[mid:])
return merge(left,right)
if __name__ == '__main__':
a = [14, 2, 34, 4, 21, 19,1]
print (merge_sort(a))
5 快速排序
快速排序首先选择一个基准值(pivot)
把比基准值小的元素放在基准值左边
把比基准值大的元素放在右边
最后的排序的数组就是:左边子数组 + 基准值 + 右边子数组
def quicksort(array):
if len(array) <2: # 基线条件:为空或者只包含一个元素的数组,它是有序的
return array
else:
pivot = array[0]
less = [each for each in array[1:] if each<=pivot]
greater = [each for each in array[1:] if each>pivot]
return quicksort(less) + [pivot] + quicksort(greater) # 记录了函数调用的状态当“归”时直接计算
快速排序的速度取决于选择的基准值
最佳情况:每层调用复杂度O(n),栈的高度O(log(n)) ,所以复杂度O(nlog(n))。
最坏的情况:每层调用复杂度O(n),栈的高度O(n) ,所以复杂度O(n**2)
最佳情况也是平均情况。
6 堆排序
首先根据序列构建一个完全二叉树
在完全二叉树的基础上,从最后一个非叶结点开始调整:比较三个元素的大小–自己,它的左孩子,右孩子。分为三种情况:
自己最大,不用调整
左孩子最大,交换该非叶结点与其左孩子的值,并考察以左孩子为根的子树是否满足大顶堆的要求,不满足递归向下处理
右孩子最大,交换该非叶结点与其右孩子的值,并考察以右孩子为根的子树是否满足大顶堆的要求,不满足递归向下处理
参考https://blog.youkuaiyun.com/u011240016/article/details/53428489/
完成了初始堆的创建之后,就可以通过不断的重建堆进行堆排序,每次重建堆就是一趟排序,每次重建时都将堆顶节点与堆无序区的最后一个元素交换,因此每趟堆排序后堆的有序区就增加了一个元素(从数组最后与各元素开始,向前排列),下轮就使用无序区组成的堆进行重建,每次重建都只是对堆顶节点的调整,因为初始堆建成之后,其他节点都满足堆的定义。
代码实现
来源:https://www.cnblogs.com/bingabcd/p/7425039.html
升序-建立最大堆,降序-建立最小堆
def sift(data, low, high):
i = low # 父节点
j = 2 * i + 1 # 左子节点
tmp = data[i] # 父节点值
while j <= high: # 子节点在节点中
if j < high and data[j] > data[j + 1]: # 有右子节点且右节点比父节点值大
j += 1
if tmp > data[j]:
data[i] = data[j] # 将父节点替换成新的子节点的值
i = j # 变成新的父节点
j = 2 * i + 1 # 新的子节点
data[i] = tmp
else:
break
# 将替换的父节点值赋给最终的父节点
def heap_sort(data):
n = len(data)
# 创建堆
for i in range(n//2-1, -1, -1):
sift(data, i, n-1)
# 挨个出数
for i in range(n-1, -1, -1): # 从大到小
data[0], data[i] = data[i], data[0] # 将最后一个值与父节点交互位置
sift(data, 0, i-1)
import random
li = [5,11,7,2,3,17]
print(li)
heap_sort(li)
print(li)
时间复杂度
创建初始堆的时间复杂度是O(n),简单的解释是有n/2个节点需要调整,每次调整节点时只是上写移动常数个节点,因此创建初始堆的时间复杂度是O(n)。而实际进行堆排序时,需要进行n趟,每趟进行堆重建时就是调整堆顶节点,最多移动次数不会超过书的高度O(log n),因此时间复杂度是O(n*log n)。
堆排序对数据的原始排列状态并不敏感,所以其最坏时间复杂度、最好时间复杂度、平均时间复杂度均是O(n*log n),堆排序不是一种稳定的排序算法。
7 希尔排序
希尔排序是直接插入排序的改进
对有序序列插入排序有:直接插入排序,折半插入排序,希尔排序,后两个是改进。
L = [1, 3, 2, 32, 5, 4,7]
def Shell_sort(L):
step = len(L)//2
while step > 0:
for i in range(step,len(L)):
while(i >= step and L[i] < L[i-step]):
i -= step
step = step//2
print(L)
Shell_sort(L)
本文详细介绍了Python 3中实现的七大排序算法,包括冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序和希尔排序。详细阐述了每个算法的原理,并给出了具体实现代码,帮助读者理解各种排序算法的时间复杂度和应用场景。
1088

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



