一:冒泡排序
时间复杂度:O(n2)
原理:
(1):相邻元素互相比较 如果第一个比第二个大 就交换两者的位置
(2):对每一对邻居做比较 从头走到尾 即走了一趟 最后一位元素即为最大的元素
(3):针对所有的元素重复以上步骤 除了最后一个(因为最后的一位元素已经选出来了 为最大的元素 不需要再比较)
(4):持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较,最后数列就是从大到小一次排列
例如:
无序的序列:[54,26,93,17,77,31,44,55,20]
第一次比较,54>26,交换:26,54, 93,17,77,31,44,55,20第二次比较,54<93,不处理:26,54, 93,17,77,31,44,55,20第三次比较,93>17, 交换:26,54, 17,93, 77,31,44,55,20第四次比较,93>77, 交换:26,54, 17, 77, 93, 31,44,55,20第五次比较,93>31, 交换:26,54, 17, 77, 31,93, 44,55,20第六次比较,93>44, 交换:26,54, 17, 77, 31, 44,93, 55,20第七次比较,93>55, 交换:26,54, 17, 77, 31, 44, 55,93, 20第八次比较,93>20, 交换:26,54, 17, 77, 31, 44, 55, 20,93小结:1、最大的数93排在了队列的末尾2、列表的长度n = 9,我们比较了n-1次
重复上面的过程:26, 17,54, 31, 44, 55, 20, 77,93小结:1、比较了n-2次2、77放在了无序列表尾部
继续:17, 26, 31, 44, 54, 20,55, 77,93 #比较了 n-3 ,把55放在了无序列表的尾部
17, 26, 31, 44, 20, 54,55, 77,93 #比较了 n-4 ,把54放在了无序列表的尾部
17, 26, 31, 20, 44, 54,55, 77,93 #比较了 n-5 ,把54放在了无序列表的尾部
17, 26, 20, 31, 44, 54,55, 77,93 #比较了 n-6 ,把31放在了无序列表的尾部
17, 20, 26, 31, 44, 54,55, 77,93 #比较了 n-7 ,把26放在了无序列表的尾部
17, 20, 26, 31, 44, 54,55, 77,93 #比较了 n-8 ,得到一个有序的序列
总结:
相邻元素两两比较把最大值排在无序序列尾部这个过程,要循环n-1
案例
from cal_time import *
importrandom
@cal_timedefbubble_sort(li):#li 传入的无序列表
for i in range(len(li) - 1): #i表示执行多少趟
for j in range(len(li) - i - 1): #第i趟 无序区范围[0, n-i-1] j表示箭头即倒数第二个位置 0~n-i-2 n代表列表长度
if li[j] > li[ j + 1]: #如果当前位置比邻居位置大
li[j] , li[j + 1] = li[j+1], li[j]returnli
li= list(range(100))
random.shuffle(li)print("pre:", li)
bubble_sort(li)print("after:", li)
冒泡排序
冒泡排序
冒泡排序
优化版本:当某一趟走完以后发现并没有进行数据交换,那么此时的数列已经排列好了,没有必要在进行下去。例如:极端情况下,数列本来已经排序好的,我们只需要走一趟即可完成排序。
from cal_time import *
importrandom
@cal_timedefbubble_sort(li):#li 传入的无序列表
for i in range(len(li) - 1): #i表示执行多少趟
exchange = False #交换标志
for j in range(len(li) - i - 1): #第i趟 无序区范围[0, n-i-1] j表示箭头即倒数第二个位置 0~n-i-2 因为顾头不顾尾 因此范围为len - i - 1 n代表列表长度
if li[j] > li[ j + 1]: #如果当前位置比邻居位置大
li[j] , li[j + 1] = li[j+1], li[j]
exchange= True #改变标志
if notexchange:return
returnli
li= list(range(100))
random.shuffle(li)print("pre:", li)
bubble_sort(li)print("after:", li)
优化版冒泡排序
优化版冒泡排序
优化版冒泡排序
二:选择排序
时间复杂度:O(n2)
原理:
(1)每次从列表中选出一个数作为比较的参数(最大或者最小),并且将其与其余数字进行比较
(2)若列表中的某个数字比选中的元素小 则二则交换位置
(3)依次将列表进行循环 选出最小的数 放在最左边
(4)重复上述步骤 直至完成排序
例如:
无序的序列:[54,26,93,17,77,31,44,55,20]
第一次,从无序序列中挑出一个最小值,放在有序序列的尾部:17,54,26,93, 77,31,44,55,20第二次,从无序序列中挑出一个最小值,放在有序序列的尾部:17,20,54,26,93, 77,31,44,55第三次,从无序序列中挑出一个最小值,放在有序序列的尾部:17,20, 26,54, 93, 77,31,44,55第四次,从无序序列中挑出一个最小值,放在有序序列的尾部:17,20, 26, 31,54, 93, 77, 44,55第五次,从无序序列中挑出一个最小值,放在有序序列的尾部:17,20, 26, 31, 44,54, 93, 77, 55第六次,从无序序列中挑出一个最小值,放在有序序列的尾部:17,20, 26, 31, 44,54, 93, 77, 55第七次,从无序序列中挑出一个最小值,放在有序序列的尾部:17,20, 26, 31, 44,54, 55, 93, 77第八次,从无序序列中挑出一个最小值,放在有序序列的尾部:17,20, 26, 31, 44,54, 55, 77, 93
案例
from cal_time import *
importrandom#选出最小元素的位置索引
defget_min_pos(li):
min_position_index= 0 #设置初始位置索引
for i in range(1, len(li)): #循环列表
if li[i] < li[min_position_index]: #比较循环出来的数值 与初始位置数值大小
min_position_index = i #如果 循环出来的数值小于初始位置 则两则调换下位置
return min_position_index #返回上述索引
@cal_timedefselect_sort(li):for i in range(len(li) - 1): #循环n次或者n - 1次都可以
min_index = i #记录循环开始最小的数值索引
#此时无序区的范围为 [i - len(li)] 因为每次循环都会选出一个最小的放在最左边 下次开始在一个新的列表进行循环
for j in range(i + 1, len(li)): #新的列表开始位置为i 自己不需要与自己比 因此从 i + 1开始进行计算
if li[j] < li[min_index]: #比较初始索引值 与循环出来的值大小
min_index = j #如果上述条件成立 则循环出来的数小于初始值数 因此换下位置
li[i], li[min_index] = li[min_index], li[i] #最后进行位置互换
data_list= list(range(100))
random.shuffle(data_list)#打乱列表数据
print("pre:", data_list)
select_sort(data_list)print("after:", data_list)
选择排序
三:插入排序
时间复杂度:O(n2)
原理:
(1):从第一个元素开始,该元素可以认为已经被排序
(2):取出下一个元素,在已经排序的元素序列中从后向前扫描
(3):如果被扫描的元素(已经排序元素)大于当前取出的元素 则将已经被扫描的元素向后挪
(4):重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
(5):将新元素插入到该位置后
例如:
无序的序列:[54,26,93,17,77,31,44,55,20]
第一次插入:[20,54,26,93,17,77,31,44,55]
第二次插入:[20,54,55,26,93,17,77,31,44]
第三次插入:[20,44,54,55,26,93,17,77,31]
第四次插入:[20,31,44,54,55,26,93,17,77]
第五次插入:[20,31,44,54,55,77,26,93,17]
第六次插入:[17,20,31,44,54,55,77,26,93]
第七次插入:[17,20,31,44,54,55,77, 93,26]
第八次插入:[17,20,26,31,44,54,55,77, 93]
总结:
待排序序列元素数 : n
插入的次数: n-1
案例
importrandomdefinsert_sort(li):for i in range( len(li) ): #无序区域数据
temp = li[i] #临时摸到的数字
j= i - 1 #当前摸到的数字 左边的元素大小
while j >= 0 and li[j] > temp: #保证左边的元素最起码有值 同时左边的元素大于当前摸到的数
li[j + 1] = li[j] #当前的牌 与左边的牌换下位置
j = j - 1 #然后j减去一 继续与有序区左边比
li[j + 1] = temp #左边已经有序 右边为无序 即 左边有序去 + 1
data_list= list(range(30))
random.shuffle(data_list)#打乱列表数据
print("pre:", data_list)
insert_sort(data_list)print("after:", data_list)
插入排序
四:快速排序
时间复杂度:平均O(nlogn)
原理:
(1):从数列中随机选取一个数作为基数
(2):使基数处于中间 比基数小的放在左边 比基数大的放在右边 与基数相等的放在左右都行
(3):递归完成上述操作 完成排序
例如:
快速排序
无序的序列:[54,26,93,17,77,31,44,55,20]
第一次快速排序,那第一个元素54作为基准值,进行分割:
[26,17,31,44,20,54, 93,77,55]
经过第一次分割排序,基准值左边的序列的所有元素都小于基准右边序列的所有元素
对54 左边的序列进行递归的分割:
拿26作为基准值,进行分割:
[17,20,26, 31,44,54, 93,77,55]
对26 左边的序列进行递归的分割:
拿17作为基准值,进行分割:
[17,20,26, 31,44,54, 93,77,55]
对17 右边的序列进行递归的分割:
右边序列只有一个元素20,不能在分割,返回
对26 右边的序列进行递归的分割:
拿31作为基准值,进行分割:
[17,20,26, 31,44,54, 93,77,55]
31小于44,不处理
对54 右边的序列进行递归的分割:
拿93作为基准值,进行分割:
[17,20,26, 31,44,54, 77, 55,93]
对93 右边的序列进行递归的分割:
拿77作为基准值,进行分割:
[17,20,26, 31,44,54, 55, 77,93]
接下来,把整个序列进行排序:
[17,20,26, 31,44,54, 55, 77,93]
案例
importrandomdefpartition(li, left, right):''':param li: 排序的数组
:param left: 基数左边索引元素
:param right: 基数右边索引元素
:return:'''temp= li[left] #从左边选取一个基数
while left = temp: #右边小于左边 且右边值大于基数
right = right - 1 #右边值大于基数 索引向向左边移动与基数比较
li[left] = li[right] #此时左边有位置 将右边的元素移动到左边
while left < right and li[left] <= temp: #判断左边值与基数的大小
left = left + 1 #左边值小于基数 索引向右边移动继续与基数比较
li[right]= li[left] #此时右边有位置 将左边的元素移动到右边
li[left]= temp #将基数放到中间
return left #返回基数索引
defquick_sort(li, left, right):''':param li: 排序的数组
:param left: 基数左边索引元素
:param right: 基数右边索引元素
:return:'''
if left < right: #中止条件 排序区域至少 有两个元素
mid = partition(li, left, right) #基数
quick_sort(li, left, mid - 1) #循环排序基数左边
quick_sort(li, mid + 1, right) #循环排序基数右边
if __name__ == '__main__':
li= list(range(10))
random.shuffle(li)print("pre:", li)
quick_sort(li,0,len(li)- 1)print("after:", li)
快速排序
defquick_sort(li):if len(li) < 2: #如果列表长度小于2 说明只有 0 或者1 一个元素没有办法比较 不需要再进行排序
returnli
temp=li[0]
left= [v for v in li[1:] if v < temp] #生成一个左边的列表
right = [v for v in li[1:] if v > temp] #生成右边的列表
left =quick_sort(left)
right=quick_sort(right)return left + [temp] + right #进行拼凑
切分快速排序
五:堆排序
堆:本质是一个完全二叉树 如果根节点的值是最小的值 则称之为小根堆 如果根节点的值为最大值 则称之为大根堆
时间复杂度:平均O(nlogn)
二叉树:度不超过二的数(节点最多有两个叉)
满二叉树:一个二叉树 每个节点都达到最大值
完全二叉树:叶节点只能出现在最下层和次下层 并且最下面一层的节点都集中在该层最左边的位置
节点关系:
(1)父节点和左孩子节点索引关系
1:父节点(9)索引0 -----> 左边子节点(8)索引1 右边子节点(7)索引22:父节点(8)索引0 -----> 左边子节点(6)索引3 右边子节点(5)索引4
关系式:
父节点索引为i
左边子节点为2 * i + 1右边子节点为2 * i + 2
子节点寻找父亲 (i - 1) // 2
原理:
(1):将待排序数据列表建立成堆结构(建立堆)
(2):通过上浮或者下沉等操作得到顶端最大的元素(大根堆为例)
(3):去掉顶部元素 将最后面的一个元素放到堆顶 再次进行调整 使得堆顶最大元素
(4):重复上述步骤 直到堆顶为空
importrandomdefsift(li, low, high):
temp= li[low] #存放根节点
i = low #根节点索引
j = 2 * i + 1 #根节点对应左边孩子的索引
while j <= high: #j要小于最后一个元素 最后一个元素索引最大 如果大于high说明不在该堆
if j + 1 < high and li[j] < li[j + 1]: #右节点必须有值 且比左节点大
j = j + 1 #指向右节点
if li[j] > temp: #比较当前元素与根节点大小
li[i] = li[j] #当前节点大于根节点 则当前节点更改为根节点
i = j #原来j的位置被更改为根节点
j = 2 * i + 1
else:#否则说明本次 没有叶子节点大于根节点 结束循环
breakli[i]= temp #最后将被调整节点的值放到i节点上(空出的位置)
defheap_sort(li):
n=len(li)#构造堆
for low in range((n - 2) // 2, -1, -1):'''low:代表根节点 即最后一个元素的父节点 最后一个元素为n-1 带人到 (i-1)//2 可知根节点公式为(n - 2) // 2
到达-1 前包后不包 即到达最后一个位置'''sift(li, low, n- 1)#挨个出数
for high in range(n - 1, -1, -1):'''high : 代表最后一个元素 最后一个元素为n-1'''li[0], li[high]= li[high], li[0] #将最后一个元素依次与根节点调换位置
sift(li, 0, high- 1) #每次交换一个根节点 即high左边的为新的high
data_list= list(range(100))
random.shuffle(data_list)#打乱列表数据
print("pre:", data_list)
heap_sort(data_list)print("after:", data_list)
堆排序
六:归并排序
时间复杂度:O(nlogn)
原理:
(1):申请空间 使其大小为两个已经排序序列之和 该空间用来存放合并和的序列
(2):设定两个索引 最初位置都是为两个已经排序序列的起始位置
(3):比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
(4):重复步骤3直到某一指针达到序列尾
(5):将另一序列剩下的所有元素直接复制到合并序列尾
importrandomdefmerger(li, low, mid, high):''':param li: 排序列表
:param low:列表开头位置
:param mid:中间分割
:param high:列表结束位置
:return:'''i= low #第一个列表开头索引
j = mid + 1 #第二个列表开头索引
temp =[]while i <= mid and j <= high: #分割列表有值才可以进行循环
if li[i] < li[j]: #判断两个列表的首位大小
temp.append(li[i]) #小的加入新的列表
i = i + 1 #在原有的数值上 将索引右移
else:
temp.append(li[j])
j= j + 1
while i <=mid:
temp.append(li[i])
i= i + 1 #在原有的数值上 将索引右移
while j <= high: #左边分割有剩余
temp.append(li[j])
j= j + 1li[low:high+ 1] = temp #最后将temp中的数写入到原来的列表中
defmerger_sort(li,low,high):if low < high: #至少有两个元素
mid = (low + high) // 2merger_sort(li,low,mid)#分割中间元素左边列表
merger_sort(li,mid + 1 ,high) #分割中间元素右边列表
merger(li,low,mid,high) #合并
data_list= list(range(100))
random.shuffle(data_list)#打乱列表数据
print("pre:", data_list)
merger_sort(data_list,0,len(data_list)- 1)print("after:", data_list)
归并排序
defmerger(l1,l2):''':param l1: 列表1
:param l2: 列表2
:return:'''li=[]
i=0
j=0while i < len(l1) and j
li.append(l1[i])
i= i + 1
else:
li.append(li[j])
j= j + 1
while i
li.append(l1[i])
i= i + 1
while j
li.append(l2[j])
j+= 1
return li
多列表归并排序
七:希尔排序
原理:
(1):先选取一个小于n(列表长度)的整数d1作为第一个增量,所有的数据全部记录分组 把所有距离为d1倍数的数据放在一个组
(2):在各组内先进行排序
(3):取第二个增量d2
importrandomdefshell_sort_gap(li,gap):
n=len(li)for i inrange(gap,n):
temp= li[i] #当前分割元素
j = i - gap #当前分割元素左边位置
while j >= 0 and li[j] > temp: #左边位置必须有值 且比当前分割元素大
li[j + gap] = li[j] #则左边元素右移
j = j - gap #继续比较左边元素
li[j + gap] =tempdefshell_sort(li):
gap= len(li) // 2
while gap >0:
shell_sort_gap(li,gap)
gap= gap//2data_list= list(range(100))
random.shuffle(data_list)#打乱列表数据
print("pre:", data_list)
shell_sort(data_list)print("after:", data_list)
希尔排序
PS:此排序比较鸡肋 比插入排序快 比快排慢
八:基数排序
时间复杂度:平均、最好、最坏都为O(k*n),其中k为常数,n为元素个数
原理:
(1):基数排序的思想就是先排序好个位,十位依次类推一直便利到最大位置 排序结束
(2):基数排序不是比较排序 而是通过分配和收集的过程来实现排序
(3):初始化是个捅(固定的) 下标记为0-9
(4):通过得到待排序的十百位数字 把这个数字放到对应的item中
importrandomdefget_digit(num, i):'''输入0 ---> 拿到个位
输入1 ---> 拿到十位
一次类推
例如:
num = 12345
i = 0
当i等于0的时候 10 ** i = 1
12345 整除1得到12345
在取余数得到5 即个位数
当i等于1的时候 10 ** i = 10
12345 整除10得到1234
在取余数得到4 即十位数'''
return num // (10 ** i) % 10
defradix_sort(li):
max_num=max(li)
i=0while (10 ** i <=max_num):
buckets= [[] for _ in range(10)] #生成 0 - 9 的桶
for value inli:
digit= value // (10 ** i) % 10 #此代码看上面的函数
buckets[digit].append(value) #将取出来的数字加入对应的桶中
li.clear() #清空原表
for bucket inbuckets:for value inbucket:
li.append(value)
i= i + 1 #每次加一获取十位 百位
data_list= list(range(100))
random.shuffle(data_list)#打乱列表数据
print("pre:", data_list)
radix_sort(data_list)print("after:", data_list)
基数排序