今日复习内容:基础算法中的选择排序/插入排序/快速排序/归并排序/桶排序
一.选择排序
1.算法步骤
- 从左往右找到最小的元素,放在起始位置
- 重复上述步骤,依次找到第二,第三小的元素
2.具体描述
- 给定一个长度为n的列表,算法循环n-1次后可以得到有序序列
- 第0次循环,从[0,n-1]中找出最小值a[x],与a[0]交换
- 第1次循环,从[1,n-1]中找出最小值a[x],与a[1]交换
- 第2次循环,从[2,n-1]中找出最小值a[x],与a[2]交换
- 第i次循环,从[i,n-1]中找出最小值a[x],与a[i]交换
- 第n-2次循环,从[n-2,n-1]中找出最小值a[x],与a[n-2]交换
- 时间复杂度O(n^2),空间复杂度O(1),稳定
上一篇的那个题也可以用这种方法做,具体如下:
题目描述:
在一个神秘的岛屿上,有一支探险队发现了一批宝藏,这批宝藏是以整数数组的形式存在的,每个宝藏上都标有一个数字,代表了其珍贵程度。然而,由于某种神秘力量,这批宝藏的顺序被打乱了,探险队需要将宝藏按照其珍贵程度进行排序,以便更好地保护和研究它们。作为探险队的一员,肖恩需要设计合适的排序算法来将宝藏按照其珍贵程度从小到大进行排序,请你帮帮他。
输入描述:
输入第一行包括一个数字n,表示宝藏一共有n个;
输入第二行包括n个数字,第i个数字 a[i]表示第i个宝藏的珍贵程度;
数据保证1 < n < 1000,1 < a[i] < 10^6
输出描述:
输出n个数字,为对宝藏按照其珍贵程度从小到大进行排序的结果
以下是参考答案:
n = int(input())
a = list(map(int,input().split()))
# 循环n-1次
for i in range(0,n-1):
min_value = a[i]
min_idx = i
for j in range(i,n):
if a[j] < min_value:
min_value = a[j]
min_idx = j
a[i],a[min_idx] = a[min_idx],a[i]
print(' '.join(map(str,a)))
运行结果:
二.插入排序
1.算法步骤
- 第一个元素看作已排序,从左往右遍历每一个元素
- 在已排序元素中从后往前扫描,如果当前元素大于新元素,则当前元素向后移一位
- 重复第二步,直到找到小于等于新元素为止
2.具体描述
- 将上述步骤看作摸牌,每次摸一张牌从后往前判断是否可以插入
- 对于第i张牌a[i], [0,i-1]中的数都是已经排序好的
- 从后往前逐个判断a[j]是否大于a[i],
- 如果a[j]大于a[i],则a[j]向后移一个位置
- 如果a[j]小于等于a[i],则在a[j+1]的位置放置a[i]
- 时间复杂度O(n^2),空间复杂度O(1),不稳定
仍然是那个题,题目请看上面,过程如下:
n = int(input())
a = list(map(int,input().split()))
# 循环n-1次
for i in range(1,n):
value = a[i]
insert_idx = 0
for j in range(i-1,-1,-1):
if a[j] > value:
a[j+1] = a[j]
else:
insert_idx = j + 1
break
a[insert_idx] = value
print(' '.join(map(str,a)))
运行结果:
三.快速排序
1.算法步骤
- 找一个基准值x
- 把列表分为三部分:小于等于x的,x,大于x的
- 左半部分和右半部分递归使用该策略
2.化抽象为具体
例:a = [3,5,8,1,2,9,4,7,6] ,left = 0,right = 8
- 设置基准值下标为left
- 存放小于等于基准值下标为idx = left + 1
- 从left + 1 到right中的每一个元素:
如果a[i]小于等于基准值,则将a[i]和a[idx]互换,idx +=1
- 最终结果:[1,2] [5] [5,8,9,7,6]
- 左侧和右侧重复上述操作
- 时间复杂度:O(nlog(n)),空间复杂度:O(nlog(n))
现在我把它转换成代码:
# 列表a,左端点为left,右端点为right
# [left,right]
def partition(a,left,right):
# 找一个基准值,将列表分成三部分
# 标准中为a[left]
idx = left + 1
for i in range(left + 1,right + 1):
# 如果元素小于基准值,就放到前面去
if a[i] <= a[left]:
a[i],a[idx] = a[idx],a[i]
idx += 1
# 把前半部分最后一个值和基准值交换
a[left],a[idx- 1] = a[idx - 1],a[left]
return idx - 1
# 将a[left,right]进行排序
def quicksort(a,left,right):
# 我们把它打印出来
if left < right:
mid = partition(a,left,right)
# 此时a被分成了三部分:a[left,mid - 1],a[mid],a[mid + 1,right]
quicksort(a,left,mid - 1)
quicksort(a,mid + 1,right)
n = int(input())
a = list(map(int,input().split()))
quicksort(a,0,n - 1)
print(' '.join(map(str,a)))
运行结果:
(这也是“宝藏排序”那个题的第四种做法,代码自己编的,仅供参考)
四.归并排序
- 首先考虑一个问题,两个有序列表怎样合并成一个列表?
- 比如:a = [1,2,3],b = [4,5,6]
- 方法如下:
1.构建一个空列表result[]
2.当a非空且b非空
比较a[0]和b[0]
result 添加较小的那个元素,同时该元素从原始数组中弹出
3.如果a非空但b是空集,则将a添加到result末尾
4.如果b非空但a是空集,则将b添加到result末尾
现在我把它编成代码:
a = [1,2,3]
b = [4,5,6]
def Merge(a,b):
result = []
while len(a) != 0 and len(b) != 0:
if a[0] < b[0]:
result.append(a.pop(0))
else:
result.append(b.pop(0))
result.extend(a)
result.extend(b)
return result
print(Merge(a,b))
运行结果:
1.算法步骤
- 先把数组分成两部分
- 每部分递归处理变成有序
- 将两个有序列表合并起来
“宝藏排序”那个题的第五种做法:
def Merge(a,b):
result = []
while len(a) != 0 and len(b) != 0:
if a[0] < b[0]:
result.append(a.pop(0))
else:
result.append(b.pop(0))
result.extend(a)
result.extend(b)
return result
def Mergesort(a):
if len(a) < 2:
return a
mid = len(a) // 2
left = Mergesort(a[:mid])
right = Mergesort(a[mid:])
return Merge(left,right)
n = int(input())
a = list(map(int,input().split()))
a = Mergesort(a)
print(' '.join(map(str,Mergesort(a))))
运行结果:
五.桶排序
- 利用函数映射关系,将输入数据分到有限的桶里,然后每个桶分别排序
- 初始化K个桶
- 遍历数据,将数据放入对应桶中
- 每个桶单独排序
- 各个桶是数据拼接起来
“宝藏排序” 的第五种方法:
def Bucket_Sort(a,bucketcount):
minvalue,maxvalue = min(a),max(a)
# 桶大小,每个桶的元素范围
bucketsize = (maxvalue - minvalue + 1) // bucketcount
res = [[] for _ in range(bucketcount + 1)]
# 把所有元素放到对应桶中
for x in a:
# 把元素放到第几个桶中
idx = (x - minvalue) // bucketsize
res[idx].append(x)
print(res)
# 每个桶单独排序
ans = []
for res_x in res:
res_x.sort()
ans += res_x
return ans
n = int(input())
a = list(map(int,input().split()))
a = Bucket_Sort(a,min(1000,n))
print(' '.join(map(str,a)))
运行结果:
OK,今天就写到这里,我有点头晕了,这几种方法全部理解透彻有点 费命。
下次继续!
(若有错误,欢迎各位指出)