常见排序算法
先简单讲解一下如何交换两个变量的值
- 创建临时变量
- 直接交换(a,b = b,a)
例:
临时变量:
c = a
a = b
b = c
#直接交换
a , b = b , a
冒泡排序
两两比较,找最大值换到最后,再找次大值,放次之
以下以找最大值为例子:
# 冒泡排序 大数上浮法、小数下沉法(每次找最小值往最前边排或每次找最大值往最后排)
arr = [30,8,-10,22,26,3,2,9,11]
def bubble_sort(arr):
for i in range(0, len(arr)-1): #为了控制比较操作的循环次数
for j in range(0, len(arr)-1-i): #选择操作的元素(索引下标)
if arr[j] >= arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
else:
pass #空语句,防止报错
return arr
if __name__ == '__main__': #python的main函数 防止被别的模块导入时执行
a = bubble_sort(arr)
print(a)
结果:
[-10, 2, 3, 8, 9, 11, 22, 26, 30]
图解:
选择排序
每次找最小值,以假设的最小值进行位置互换。(循环后面全部找出最小值后换)
[30,8,-10,22,26,3,2,9,11]
第一次 假设30最小,在其他当中找最小值,即-10,进行位置互换
[-10,8,30,22,26,3,2,9,11]
第二次 假设8最小,在其他当中找最小值,即2,进行位置互换
[-10,2,30,22,26,3,8,9,11]
# 选择排序
# 类似于冒泡排序,每一次找到最小值
arr = [8,3,2,6,1,4,9,7]
def xuanze(arr):
for i in range(0, len(arr)-1): #控制循环的次数(即开始的起点)
min = i #假定第一个为最小值(创建个临时变量)
for j in range(i+1, len(arr)): #从开始起点循环到结束
if arr[j] < arr[min]: #如果小则交换,将下标交换
min = j
if i != min: #循环结束,判断是否后面有小值,有则交换
arr[i], arr[min] = arr[min], arr[i]
return arr
if __name__ == '__main__':
a = xuanze(arr)
print(a)
结果:
[1, 2, 3, 4, 6, 7, 8, 9]
插入排序
默认第一个值为有序的,之后一次插入后面的每一个值(多次比较),最终得出排序结果。保证 i 前面的数都是有序
例:
[6,5,3,8,7,2,4]
第一次 假设第一位(6)有序(最小),将后一位(第二位)与第一位对比,若第二位小将其插入到第一位前作为第一位
[5,6,3,8,7,2,4]
第二次 假设前两位有序,将后一位(第三位)与第二位对比,若比第二位小,再与第一位对比,若比第一位大比第二位小则将其插入到第二位前作为第二位,反之若比第一位小则将其插入到第一位前作为第一位
[3,5,6,8,7,2,4]
第三次 假设前三位有序,将后一位(第四位)与第三位对比,,若比第三位大,则保持原位,默认前四位有序,若比第三位小,再与第二位和第一位(前面有序的全部)对比,若比第二位大比第三位小则将其插入到第三位前作为第三位,反之若比第一位大比第二位小则将其插入到第二位前作为第二位,若比第一位小则将其插入到第一位前作为第一位
[3,5,6,8,7,2,4]
#插入排序
num = [11,2,12,13,1,4]
def insert_sort(num):
for i in range(0, len(num)-1): #默认有序下标(i前包括i全部有序)
for j in range(i+1, 0, -1): #i+1 往前比较
if num[j] < num[j-1]:
num[j], num[j-1] = num[j-1], num[j]
return num
insert_sort(num) #函数调用
print(num)
计数排序
计数排序(Counting Sort)是一种不比较数据大小的排序算法,是一种牺牲空间换取时间的排序算法
计数排序适合数据量大且数据范围小的数据排序,如对人的年龄进行排序,对考试成绩进行排序等
找到待排序列表中的最大值 k,开辟一个长度为 k+1 的计数列表
例(元素不重复):
原容器:
arr = [2,3,1,6,5,7]
创建新容器(7+1)
arr0 = [0, 0, 0, 0, 0, 0, 0, 0]
下标 0 1 2 3 4 5 6 7
然后找与元素相同的下标值元素加1(两个就是2)
arr0 = [0, 1, 1, 1, 0, 1, 1, 1]
然后再将为1的导出
==> [1,2,3,5,6,7] 即排序成功
#计数排序
def counting_sort(array): #如果只有一个数无需排序直接返回
if len(array) < 2:
return array
max_num = max(array)
count = [0] * (max_num + 1) #创建一个新列表,记录下标索引
for num in array: #寻找是否有元素相同的下标值,有则加一,并统计每个值的数量
count[num] += 1
new_array = list() #创建空列表导出
for i in range(len(count)): #循环次数(遍历列表全部)
for j in range(count[i]): #循环次数(相同元素的数量)
new_array.append(i) #将元素i加入new_array
return new_array
if __name__ == '__main__':
array = [5, 7, 3, 7, 2, 3, 2, 5, 9, 5, 7, 6]
print(counting_sort(array))
结果:
[2, 2, 3, 3, 5, 5, 5, 6, 7, 7, 7, 9]
二分查找
二分查找(折半查找):在有序序列中高效的查找指定的元素
#二分查找
def binary_search(list,item):
"""二分查找方法"""
left = 0 # 定义最小下标
right = len(list)-1 # 定义最大下标
while left <= right: #while循环,保证可以遍历到指定区域的元素,直到被被寻找的值和中间值相等
mid = int((left + right)/2) # 寻找数组的中间值
guess = list[mid] # 获取列表最中间的元素
if guess == item:
return mid # 进行条件判断,将中间值和被寻找的值进行比较,相等则返回该值
if guess > item:
right = mid-1 # 如果被寻找的值小于中间值,则最大下标变化为中间值的前一个元素下标
else:
left = mid + 1 # 如果被寻找的值大于中间值,则最小下标变化为中间值的后一个元素下标
return None
nums = [1,33,44,66,77,567,5677]
print(binary_search(nums,567))
结果:
5 #返回索引下标
函数
组织好的,可以重复使用的,用于实现特定功能的代码块
len(name)内置函数 len 官方写好的 input print。。。。都是内置函数
避免重复的进行代码书写,提高代码的复用率
函数的定义语法
格式:
def 函数名(参数1,参数2......):
函数体
return 语句
#return:返回函数的返回值
#return后边的代码不会再执行了哦
#函数的返回值 将结果返回给调用者
练习:
1、定义一个函数,求其绝对值
def my_abs(x):
if x < 0:
return -x
else:
return x
a = my_abs(-3)
print(a)
a = my_abs(1)
print(a)
结果:
3
1
2、定义函数,调用后输出“我叫XXX”
def out():
print("我叫XXX")
b = out()
print(b)
c = out() #函数名可以赋值给变量
c() #变量即为函数的别名,函数名+()是执行
结果:
我叫XXX
None #函数无返回值
我叫XXX
#None类型:NoneType的字面量,函数不使用return,返回的是None:返回值没有意义
#None声明无意义的变量
#name=None
#在if判断中:None===False
函数的传参
函数的参数:函数进行计算时,接受外部(调用时)提供的数据
传入参数的类型检查
def my_abs(x):
if not isinstance(x,(int,float)): #判断如果不是int与float类型报错
raise TypeError ("i'am sorry") #raise 用于在报错最后显示
if x < 0:
return -x
else:
return x
a = my_abs('a')
print(a)
结果:
Traceback (most recent call last):
File "D:\pycharm\06-笔记.py", line 39, in <module>
a = my_abs('a')
File "D:\pycharm\06-笔记.py", line 33, in my_abs
raise TypeError ("i'am sorry")
TypeError: i'am sorry #显示
返回值
事实上,函数返回的是一个元组,多个变量可以同时接收一个元组
#返回值
def sum_01():
a = 1
b = 2
return a,b #同时返回多个值用 ,隔开即可(元组也可以省略外面的括号)
a = sum_01() #因为只有一个接收变量,所以多个返回值会以元组的形式给他接收
print(a)
a,b = sum_01() #多个返回值会以元组的形式返回
print(a)
print(b)
v,b,n = (3,4,5) #多个变量(与元组元素个数相同)接收元组,依次接收
print(v)
print(b)
print(n)
结果:
(1, 2)
1
2
3
4
5
位置参数
例:两个数字相加的函数的定义
a,b:形式参数
1,2:实际参数
数量不受限制(但形式参数与实际参数数量不同会报错)形式参数与实际参数数量一致所以依次传递(即位置传参)
1、数量必须与定义时一致
2、位置必须与定义时一致
默认参数
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
a = power(1)
print(a)
结果:
1
默认值参数设置的注意点:
- 必选参数在前,默认参数在后(原因:可以把必选的传入完后,后面全部默认)
- 多个参数时,把变化小的放在后面。变化小的参数就可以作为默认值参数
好处:降低调用函数的难度
默认参数的坑:
def add_end(L=[]):
L.append('END')
return L
add_end()
add_end()
结果:
['END', 'END']
['END', 'END', 'END']
第一次调用add_end()时没有错误,第二次时,就会出现函数似乎记住了第一次调用的函数添加的end的列表
解释:
t(a)
结果:
1
[外链图片转存中...(img-OJj4Yp6U-1697433643414)]
> 默认值参数设置的**注意点**:
>
> 1. 必选参数在前,默认参数在后(原因:可以把必选的传入完后,后面全部默认)
> 2. 多个参数时,把变化小的放在后面。变化小的参数就可以作为默认值参数
>
> 好处:降低调用函数的难度
**默认参数的坑:**
```python
def add_end(L=[]):
L.append('END')
return L
add_end()
add_end()
结果:
['END', 'END']
['END', 'END', 'END']
[外链图片转存中…(img-11PgYsnB-1697433643414)]
第一次调用add_end()时没有错误,第二次时,就会出现函数似乎记住了第一次调用的函数添加的end的列表
解释:
默认参数必须指向不变对象!(数组可变)