1. 冒泡排序
1.1 算法简介
1. 比较相邻的元素,前一个比后一个大(或者前一个比后一个小)调换位置
2. 每一对相邻的元素进行重复的工作,从开始对一直到结尾对,这步完成后,结尾为做大或最小的数
3. 针对除了最后一个元素重复进行上面的步骤。
4. 重复1-3步骤直到完成排序
1.2 算法性质
1. 时间复杂度
最优:O(n) 最差:O(n^2) 平均时间复杂度:O(n^2)
2. 空间复杂度
平均空间复杂度:O(1)
3. 稳定性
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面
4. 排序方式
内排序:所有排序都在缓存中完成
1.3 动图演示
1.4 代码实现
def bubble_sort(arr):
for i in range(1,len(arr)):
for j in range(len(arr) - i):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
arr = [9,5,6,8,2,7,3,4,1]
arr = bubble_sort(arr)
print(arr)
1.5 算法优化
假如从开始的第一对到结尾的最后一对,相邻的元素之间都没有发生交换的操作,这意味着右边的元素总是大于等于左边的元素,此时的数组已经是有序的了,我们无需再对剩余的元素重复比较下去了
def bubble_sort(arr):
for i in range(1,len(arr)):
flag = True
print(arr)
for j in range(len(arr) - i):
if arr[j] > arr[j + 1]:
flag = False
arr[j], arr[j + 1] = arr[j + 1], arr[j]
if flag:
break
return arr
arr = [3,2,1,4,5,6,7,8,9,]
arr = bubble_sort(arr)
print(arr)
2. 选择排序
2.1 算法简介
1. 在未排序的列表中,假设第一个元素的位置为最大(小)元素的位置
2. 然后在未排序的列表中依次找出最大(小)元素位置,
3. 跟假设的位置进行比较,如果原有位置与找到的最大(小)位置不是同一个位置
4. 则将这两个位置的元素进行交换
5. 对这次排序的第一个元素外的其他元素重复上面动作,直至完成排序
2.2 算法性质
1. 时间复杂度
最优:O(n^2) 最差:O(n^2) 平均时间复杂度:O(n^2)
2. 空间复杂度
平均空间复杂度:O(1)
3. 稳定性
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面
4. 排序方式
内排序:所有排序都在缓存中完成
2.3 动图演示
2.4 代码实现
def selection_sort(arr):
for i in range(len(arr)):
# 假设最小值的位置
min_pos = i
for j in range(i+1,len(arr)):
# 获取真实的最小元素的位置
if arr[j] < arr[min_pos]:
min_pos = j
# 判断真实的最小位置是否与假设位置相同,如果不同交换
if min_pos != i:
arr[i], arr[min_pos] = arr[min_pos], arr[i]
return arr
arr = [9,5,6,8,2,7,3,4,1]
arr = selection_sort(arr)
print(arr)
3. 插入排序
3.1 算法简介
1. 从第一个元素开始,该元素可以认为已经被排序
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5. 将新元素插入到该位置后
6. 重复步骤2~5,直到排序完成
3.2 算法性质
1. 时间复杂度
最优:O(n) 最差:O(n^2) 平均时间复杂度:O(n^2)
2. 空间复杂度
平均空间复杂度:O(1)
3. 稳定性
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面
4. 排序方式
内排序:所有排序都在缓存中完成
3.3 动图演示
3.4 代码实现
def insertion_sort(arr):
for i in range(1,len(arr)):
temp = arr[i] # 将要插入的数据提取出来
for j in range(i,0,-1):
# 将temp与排序好的数据从后往前一次比较,如果排序好的大于temp,排序好的元素后移
if temp < arr[j - 1]:
arr[j] = arr[j - 1]
# 将空出的下标赋值给index
index = j - 1
else:
break
# 将空出的位置赋值temp
arr[index] = temp
return arr
arr = [9,5,6,8,2,7,3,4,1]
arr = insertion_sort(arr)
print(arr)
4. 希尔排序
4.1 算法简介
1. 设元数据为一组,长度为n
2. 第一次将数据分为h = n / 2组,并且将每组进行插入排序
3. 第二次将数据分为h = n / 4组,并且将每组进行插入排序
4. 以此类推
5. 直到h = 1时,用插入排序进行微调完成排序
4.2 算法性质
1. 时间复杂度
最优:O(n log^2 n) 最差:O(n log n) 平均时间复杂度:O(n log n)
2. 空间复杂度
平均空间复杂度:O(1)
3. 稳定性
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面
4. 排序方式
内排序:所有排序都在缓存中完成
4.3 动图演示
4.4 代码实现
def shell_sort(list):
n = len(arr)
gap = int(n/2)
while gap >= 1:
for i in range(gap,n):
temp = arr[i]
j = i
while j >= gap and arr[j-gap] >temp:
arr[j] = arr[j-gap]
j -= gap
arr[j] = temp
gap = int(gap/2)
return arr
arr = [9,5,6,8,2,7,3,4,1]
arr = shell_sort(arr)
print(arr)
5.快速排序
5.1 算法简介
1.首先设定一个分界值(一般是第一个值),通过该分界值将数组分成左右两部分
2.将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值
3.然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理
4.重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了
5.2 算法性质
1. 时间复杂度
最优:O(n^2) 最差:O(n log n) 平均时间复杂度:O(n log n)
2. 空间复杂度
平均空间复杂度:O(log n)
3. 稳定性
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面
4. 排序方式
内排序:所有排序都在缓存中完成
5.3 动图演示
5.4 代码实现
#arr:待排序的序列;start排序的开始index,end序列末尾的index
#对于长度为length的序列:start = 0;end = length-1
def quick_sort(arr,start,end):
if start < end:
i , j , temp = start , end , arr[start]
while i < j:
#从右开始向左寻找第一个小于temp的值
while (i < j) and (arr[j] >= temp):
j -= 1
#将小于pivot的值移到左边
if (i < j):
arr[i] = arr[j]
i += 1
#从左开始向右寻找第一个大于pivot的值
while (i < j) and (arr[i] < temp):
i += 1
#将大于pivot的值移到右边
if (i < j):
arr[j] = arr[i]
j -= 1
#循环结束后,说明 i=j,此时左边的值全都小于temp,右边的值全都大于temp
#temp的位置移动正确,那么此时只需对左右两侧的序列调用此函数进一步排序即可
#递归调用函数:依次对左侧序列:从0 ~ i-1//右侧序列:从i+1 ~ end
arr[i] = temp
#左侧序列继续排序
quick_sort(arr,start,i-1)
#右侧序列继续排序
quick_sort(arr,i+1,end)
return arr
arr = [9,5,6,8,2,7,3,4,1]
arr = quick_sort(arr, 0, 8)
print(arr)
6. 归并排序
6.1算法简介
1. 采用递归的方法,首先将待排序列分成A,B两组;然后重复对A、B序列分组
2. 直到分组后组内只有一个元素,此时我们认为组内所有元素有序,则分组结束
3. 首先依次从第一段与第二段中取出元素比较,将较小的元素赋值给temp[]
4. 重复执行上一步,当某一段赋值结束,则将另一段剩下的元素赋值给temp[]
5. 将各分组按3.4步进行合并,最终合并为一组
6. 此时将temp[]中的元素复制给arr[],则得到的arr有序
6.2 算法性质
1. 时间复杂度
最优:O(n log n) 最差:O(n log n) 平均时间复杂度:O(n log n)
2. 空间复杂度
平均空间复杂度:O(n)
3. 稳定性
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面
4. 排序方式
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行
6.3 动图演示
6.4 代码实现
def merge_sort(arr):
#声明一个长度为len(L)的空列表
temp = [None] * len(arr)
#合并
def merge_temp(arr,first,mid,last,temp):
#对i,j,k分别进行赋值
i,j,k = first,mid+1,0
#当左右两边都有数时进行比较,取较小的数
while (i <= mid) and (j <= last):
if arr[i] <= arr[j]:
temp[k] = arr[i]
i += 1
k += 1
else:
temp[k] = arr[j]
j += 1
k += 1
#如果左边序列还有数
while (i <= mid):
temp[k] = arr[i]
i += 1
k += 1
#如果右边序列还有数
while (j <= last):
temp[k] = arr[j]
i += 1
k += 1
#将temp当中该段有序元素赋值给arr待排序列使之部分有序
for x in range(0,k):
arr[first+x] = temp[x]
# 这是分组的函数
def group_array(arr,first,last,temp):
if first < last:
mid = (int)((first + last) / 2)
#使左边序列有序
group_array(arr,first,mid,temp)
#使右边序列有序
group_array(arr,mid+1,last,temp)
#将两个有序序列合并
merge_temp(arr,first,mid,last,temp)
group_array(arr,0,len(arr)-1,temp)
return arr
arr = [9,5,6,8,2,7,3,4,1]
arr = merge_sort(arr)
print(arr)
7. 堆排序
7.1 算法简介
1. 将序列依次读取构建堆(堆必须父节点大于子节点)
2. 将根节点取出赋值给序列的最后一个元素
3. 更新序列长度及堆
4.重复2.3动作直至取完堆中的值,排序完成
7.2 算法性质
1. 时间复杂度
最优:O(n log n) 最差:O(n log n) 平均时间复杂度:O(n log n)
2. 空间复杂度
平均空间复杂度:O(1)
3. 稳定性
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面
4. 排序方式
内排序:所有排序都在缓存中完成
7.3 动图演示
7.4 代码实现
def reactor_sort(arr):
#获取左右叶子节点
def get_left(i):
return 2*i + 1
def get_right(i):
return 2*i + 2
# 调整堆 arr:待调整序列 length: 序列长度 i:需要调整的结点
def adjust_reactor(arr,length,i):
#定义一个int值保存当前序列最大值的下标
largest = i
#执行循环操作:两个任务:1 寻找最大值的下标;2.最大值与父节点交换
while True:
#获得序列左右叶子节点的下标
left,right = get_left(i),get_right(i)
#当左叶子节点的下标小于序列长度 并且 左叶子节点的值大于父节点时,将左叶子节点的下标赋值给largest
if (left < length) and (arr[left] > arr[i]):
largest = left
else:
largest = i
#当右叶子节点的下标小于序列长度 并且 右叶子节点的值大于父节点时,将右叶子节点的下标值赋值给largest
if (right < length) and (arr[right] > arr[largest]):
largest = right
#如果largest不等于i 说明当前的父节点不是最大值,需要交换值
if (largest != i):
temp = arr[i]
arr[i] = arr[largest]
arr[largest] = temp
i = largest
continue
else:
break
# 构建堆
def build_reactor(arr):
length = len(arr)
for x in range(int((length-1)/2),-1,-1):
adjust_reactor(arr,length,x)
#先建立大顶堆,保证最大值位于根节点;并且父节点的值大于叶子结点
build_reactor(arr)
#i:当前堆中序列的长度.初始化为序列的长度
i = len(arr)
#执行循环:1. 每次取出堆顶元素置于序列的最后(len-1,len-2,len-3...)
# 2. 调整堆,使其继续满足大顶堆的性质,注意实时修改堆中序列的长度
while (i > 0):
temp = arr[i-1]
arr[i-1] = arr[0]
arr[0] = temp
#堆中序列长度减1
i -= 1
#调整大顶堆
adjust_reactor(arr,i,0)
return arr
arr = [9,5,6,8,2,7,3,4,1]
arr = reactor_sort(arr)
print(arr)
8. 基数排序
8.1 算法简介
1. 将列表中的元素取出,首先确定其个位上的数字,根据该数字分配到与之序号相同的桶中
2. 当序列中所有的元素都分配到对应的桶中,再按照顺序依次将桶中的元素收集形成新的一个待排序列
3. 对新形成的序列重复执行分配和收集元素中的十位、百位…直到分配完该序列中的最高位,则排序结束
8.2 算法性质
1. 时间复杂度
最优:O(nk) 最差:O(nk) 平均时间复杂度:O(nk)
2. 空间复杂度
平均空间复杂度:O(n + k)
3. 稳定性
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面
4. 排序方式
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行
8.3 动图演示
8.4 代码实现
def radix_sort(arr):
#排序的次数跟序列中最大数的位数相关
def radix_sort_nums(arr):
maxNum = arr[0]
#寻找序列中的最大数
for x in range(0,len(arr)):
if maxNum < arr[x]:
maxNum = arr[x]
#确定排序的次数
times = 0
while maxNum > 0:
maxNum = int(maxNum / 10)
times += 1
return times
#找到num从低到高第pos位的数据
def get_pos(num,pos):
return (int(num/(10**(pos-1))))%10
#从低位到高位依次执行循环
times = radix_sort_nums(arr)
#暂时存放排序结果
bucket = []
for i in range(0,10):
bucket.append([])
for pos in range(1,times + 1):
#将数据依次装入桶中
for x in range(0,len(arr)):
#求出元素第pos位的数字
j = get_pos(arr[x],pos)
#放入对应的桶中
if j != 0:
bucket[j].append(arr[x])
else:
bucket[0].append(arr[x])
arr = []
# 将已分配好的桶中数据再倒出来,此时已是对应当前位数有序的表
for i in range(0,10):
if bucket[i] != None:
for j in range(0,len(bucket[i])):
arr.append(bucket[i][j])
bucket[i] = []
return arr
arr = [9,5,6,8,2,7,3,4,1]
arr = radix_sort(arr)
print(arr)
总结
运行时进行了一下时间比较:
1万数据时的输出:
冒泡排序:6.687218999999999
选择排序:3.645136000000001
插入排序:11.615608
希尔排序:13.012008
快速排序:9.999999974752427e-07
快速排序有误:实际上并未执行
RecursionError: maximum recursion depth exceeded in comparison
归并排序:0.05638299999999674
堆排序:0.09587900000000005
基数排序:0.08150400000000246
10万数据时的输出:
冒泡排序:751.274449
选择排序:466.66974500000015
插入排序:1233.581131
希尔排序:1409.8012320000003
快速排序:1.0000003385357559e-06
快速排序有误:实际上并未执行
RecursionError: maximum recursion depth exceeded in comparison
归并排序:0.8262230000000272
堆排序:1.2036720000000969
基数排序:1.1162899999999354
从运行结果上来看,堆排序、归并排序、基数排序排序结果快。*