实验目的
- 掌握高级语言中的排序函数的使用
- 加强对于二分搜索和插入排序的理解
- 将查找和排序联系起来
- 理解排序从一维(数的大小)到多维(属性组合)的延伸
- 借助可视化工具,加深对于排序过程的印象
- 了解课外和实际应用领域的排序算法
实验原理
bisect 模块的使用
利用二分法查找排序的模块,原理是插入排序
因为二分搜索具有左右边界的条件,对应的函数也有left right修饰
bisect.bisect(a, x, lo=0, hi=None)
bisect.bisect_left(a, x, lo=0, hi=None)
bisect.bisect_right(a, x, lo=0, hi=None)
bisect.insort(a, x, lo=0, hi=None) # 执行插入排序操作
bisect.insort_left(a, x, lo=0, hi=None)
bisect.insort_right(a, x, lo=0, hi=None)
# 别名 默认情况 bisect从右边插入
bisect(a, x, lo=0, hi=None) = bisect_right(a, x, lo=0, hi=None)
insort(a, x, lo=0, hi=None) = insort_right(a, x, lo=0, hi=None)
实验内容
1.多属性排序
尽管排序在计算机中根本依据是数的大小
还是需要关注具有组合性质的数据的排序场景
I.同方向性比较
假设:某年高考成绩位次计算中,对相同分数考生依次比较其语文、数学、英语成绩,直到分出先后顺序。输出考生位次表。
sli = [("小明",120,140,120),("小刚",110,130,140),("小丽",130,120,130)]
ranking = []
def grade_sort(sli):
sli.sort(key=lambda x:(-x[1],-x[2],-x[3]))
for rank,score in enumerate(sli):
# 严格意义上讲是学号 这里用名字短暂标识一下
rank.append((rank,score[0]))
print("名次:",rank+1,"------",score[0])
grade_sort(sli)
II.异方向性比较
假设:在旅游风景区选择上,距离与评分、门票价格多因子进行排序
不考虑综合加权指数的计算,在距离相同时为用户呈现评分从高到低、价格从低到高的景区列表。
/\
/ \
/ \
/ \
# 形式如下:关注排序因子key函数的灵活性
li = [(),(),(),(),]
li.sort(key=lambda(x[1],-x[2]))
2.学生成绩分级
ABCDEF 对应40 50 60 70 80 90
if -else switch 具有明显的限制 显然不是好的方法
仔细分析一下,类似的场景其实都包含了两个关键信息。一个是分界点,另外一个是等级。
比如学生成绩分级的分界点是[60, 70, 80, 90, 100],等级是'FEDCBA'。
将成绩转换为分级的逻辑其实就是两步:
在分界点(breakpoints)中找到学生成绩对应的位置(index)返回等级(grades)对应位置(index)的结果
# codeing:utf-8
from bisect import bisect
def grade(score, breakpoints=[60, 70, 80, 90, 100], grades='FEDCBA'):
i = bisect(breakpoints, score)
return grades[i]
if __name__ == '__main__':
scores = [59, 60, 65, 70, 78, 80, 89, 90, 95, 100]
result = [grade(i) for i in scores]
print(result)
# ['F', 'E', 'E', 'D', 'D', 'C', 'C', 'B', 'B', 'A']
同样地,根据商城会员的积分来进行分级。
breakpoints = [1000, 2000, 3000]
grades = ['普通会员', '中级会员', '高级会员', 'VIP']
scores = [499, 1023, 2020, 3000, 4999]
print([grade(i, breakpoints=breakpoints, grades=grades) for i in scores])
# ['普通会员', '中级会员', '高级会员', 'VIP', 'VIP']
grades的元素应该比breakpoints多一个(可以想想原因)。
bisect模块提供了bisect_left和bisect_right两个方法,区别在于当元素相等时,返回的index是在breakpoint的左边还是右边。
例子中用的bisect.bisect就是bisect_right。
更详细的说明,请参看Python官方文档bisect — Array bisection algorithm
孤立地来看bisect,可能会觉得仅仅是二分查找而已。然而在工作中,根据场景使用bisect,往往会给你带来惊喜。这里其实还是一个应用算法的能力。上面学生成绩分级的例子就是Python官方文档提供的。
3.煎饼排序Pancake sorting
![]()
可以先找最大的饼也可以先找最小的饼
并执行一个区间翻转的操作来更新数组
def pancakeSort_max(arr):
# 每一步都是在原数组水平上操作
n = len(arr)
while n :
k = arr.index(max(arr[:n]))
# step1 翻转区间
arr = arr[:k+1][::-1]+arr[k+1:]
# step2 翻转整体(未排序部分)
arr = arr[:n][::-1]+arr[n:]
n -= 1
return arr
def pancakeSort_min(arr):
i = 0
while i < len(arr):
k = arr.index(min(arr[i:]))
#翻转区间合并已经排序好的部分
arr = arr[:i]+ arr[i:k+1][::-1] + arr[k+1:]
i+=1
return arr
import random
testArr = [random.randint(0,50) for _ in range(10)]
print("__________Before Sorted__________")
print(testArr)
print("__________After Sorted__________")
print(pancakeSort_max(testArr))
print(pancakeSort_min(testArr))
4.排序动画演示
下载手机App "算法动画详解"
在线平台https://visualgo.net/zh/sorting
实验总结
练习题:Leetcode 34
在排序数组中查找元素第一次和最后一次出现的位置
给定一个按照升序排列的整数数组nums,和一个目标值 target。
找出给定目标值在数组中的开始位置和结束位置。
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
真实的项目中,往往会采取综合建模的方式确立排序的权重因子
这种方式在各大电商平台应用广泛,可以看做是一种加权函数的模式
实验参考
https://docs.python.org/3/library/bisect.html
https://zhuanlan.zhihu.com/p/71895688