python冒泡排序算法时间复杂度为nlogn_python常见排序算法

本文详细介绍了Python中的冒泡排序算法,包括其时间复杂度、原理及优化版实现。同时,提到了选择排序、插入排序、快速排序、堆排序、归并排序和希尔排序的基本概念和时间复杂度,展示了各种排序算法的Python实现示例。
摘要由CSDN通过智能技术生成

一:冒泡排序

时间复杂度: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)

基数排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值