python實現常見的排序算法
冒泡排序
#冒泡排序
def bubble_sort(li):
for i in range(len(li) - 1):
exchange = False
for j in range(len(li) - i - 1):
if li[j] > li[j+1]: # > 號升序 <號降序
li[j],li[j+1] = li[j+1],li[j] # 調換位置
exchange = True
if not exchange:
return
選擇排序
#選擇排序
def select_sort(li):
for i in range(len(li)-1): #一共需要遍歷的次數,因爲最後一個元素時,不需要排序,n-1次
min_loc = i #無序區第一個元素的位置,記做最小值
for j in range(i+1,len(li)): #從無序區第二個元素開始遍歷到結尾
if li[min_loc] > li[j]:#找到最小值得索引
min_loc = j
li[i],li[min_loc] = li[min_loc],li[i] #每次排序結束後,將無序區第一個元素與最小值得元素替換位置
print(li)
插入排序
#插入排序
def insert_sort(li):
for i in range(1,len(li)): # 循環n-1次,而且無序區元素是從索引爲1的元素開始,到結尾,依次插入有序區中
tmp = li[i] # 保存要插入的元素
j = i -1 # j爲有序區最後一個元素,和tmp進行比較
while j >= 0 and tmp < li[j]: # 如果tmp比j元素小,則j向前移動一位
li[j+1] = li[j]
j -= 1
li[j+1] = tmp # tmp比j元素大時,插入在j元素右邊
快速排序
def partition(li,left,right):
tmp = li[left] #獲取最左邊一個值,將其歸位
while left < right: #通過控制left和right
#從最右邊開始,向左找,找一個比tmp小的數,放在tmp的位置上
while left < right and tmp <= li[right]: #當tmp比右邊值大或等於,右邊遊標向左移動一位
right -= 1
li[left] = li[right] #將右邊比5小的數放入左邊
#從左邊向右邊找,找比tmp大的數,放入右邊這個空裏
# print("左",li)
while left < right and tmp >= li[left]:
left += 1
li[right] = li[left]
# print("右",li)
li[left] = tmp #當左右遊標重合時,就是中間位置,將該元素放入中間位置
return left
def re_quick_sort(li,left,right): #用於遞歸
if left < right: #最少兩個元素,一個元素不用排序
mid = partition(li,left,right) #將第一個元素歸位到中間,獲取其索引
re_quick_sort(li,left,mid-1) #排序左邊
re_quick_sort(li,mid+1,right) #排序右邊
堆排序
#堆排序
#大根堆
def sift(li,low,high):
'''
:param li: 傳入需要調整的堆
:param low: 堆頂,堆的根節點
:param high: 堆底,堆中最後一個元素
:return:
'''
i = low # i指向領導位置,需要放入元素的位置
j = 2 * i + 1 # j指向i的左孩子結點
tmp = li[low] # tmp指向需要調整元素的值
while j <= high: # j的元素索引不能超過元素的最後一個索引
# 找左右孩子中最大的孩子的索引 j + 1爲右孩子
if j + 1 <= high and li[j+1] > li[j]:
j += 1 # 如果右孩子比左孩子大,取右孩子結點
if li[j] > tmp: # 把 j 指向的最大的孩子結點和 tmp需要調整的元素值進行比較,
li[i] = li[j]
# j元素進入領導層,將i指向j的空位上,j是i的孩子節點
i = j
j = 2 * i + 1
else:
li[i] = tmp # 如果tmp需要調整的元素值更大,則將其放入i這個空位上,結束循環
break
else:
li[i] = tmp # 如果j比high大,代表i是葉子節點的空位,放入tmp調整元素
#小根堆
def sift_min(li,low,high):
i = low # 需要的替換位置
j = i * 2 + 1 # 左節點
tmp = li[low]
while j <= high:
#找左右子節點中最小的節點 的索引
if j + 1 <= high and li[j+1] < li[j]:
j += 1
# 判斷替換元素和子節點誰大
if li[j] < tmp: # 如果j索引坐上領導位,空位索引給i,j爲i的子元素
li[i] = li[j]
i = j
j = i * 2 + 1
else: # 位置找到了
li[i] = tmp
break
else: # j索引超過了i,i爲葉子節點時候
li[i] = tmp
def heap_sort(li): # 建堆
n = len(li) - 1 # 獲取最後一個元素索引
for i in range((n - 1)//2,-1,-1): # 最後一個非葉子節點 ( n - 1 )//2 從後向前找 從索引爲(n - 1)//2的元素找到索引爲0的元素
sift_min(li,i,n)
#每次從頂部取一個數,和底部最後一個元素交換,然後向下調整。每次底部減少一個數
for i in range(n,-1,-1): # 最後一個元素索引一直在變化,所以從後向前
li[0],li[i] = li[i],li[0]
sift_min(li,0,i-1) #最上面的元素始終爲0,最後一個元素爲i-1,因爲i的元素已經被拿出來了
print(li)
歸併排序
# 歸併排序
def merge(li,low,mid,high):
'''
:param li: 兩個有序列表組成的一個列表
:param low: 列表最左端
:param mid: 列表中間位置,左邊最後一個元素的位置
:param high: 列表最右邊
:return:
'''
i = low # 左邊遊標
j = mid + 1 # 右邊遊標
tmpArr = []
while i <= mid and j <= high: # 兩個遊標都不能超過他們最後一個元素
if li[i] < li[j]: # 左邊比右邊小,將左邊元素放入臨時數組中
tmpArr.append(li[i])
i += 1 # 將左邊遊標向後移動一位
else:
tmpArr.append(li[j])
j += 1
#將剩下的元素放入臨時列表中
while i <= mid:
tmpArr.append(li[i])
i += 1
while j <= high:
tmpArr.append(li[j])
j += 1
#將排好序的列表放入原列表中
li[low:high+1] = tmpArr
def merge_sort(li,low,high):
if low < high: # 保證進入遞歸的條件:始終有兩個元素 0 1 2 3
mid = (low + high) // 2
merge_sort(li,low,mid) # 先將左右兩邊排序
merge_sort(li,mid+1,high)
merge(li,low,mid,high) # 將左右兩邊排好序的列表歸併
希爾排序
#分組插入 每個分組間插入
def insert_sort_gap(li,gap):
for i in range(gap,len(li)): #初始位置都是從gap位置開始
j = i - gap # j取這個分組的最後一個元素
tmp = li[i] # 將這個值存儲下來
while j >= 0 and li[j] > tmp: # j索引有效,且j當前指的元素比要插入的元素大
li[j+gap] = li[j] # j位置空下來,把j的值向右移動一個
j -= gap # j指向向左一個元素
li[j+gap] = tmp
#希爾排序
def shell_sort(li):
d = len(li)//2 #第一次分組,每組的大小爲元素的一半
while d >= 1:
insert_sort_gap(li,d) #分一次組排一次序
d //= 2 # 每次分組,每組的元素個數都減少一半
計數排序
#計數排序
def count_sort(li,max_count=100):
count = [0 for i in range(max_count+1)] #生成一個全0的列表,範圍0-max_count
# print(len(count))
for val in li:
count[val] += 1 # 遍歷原列表,將每個數都統計到新列表中
li.clear() # 清除原來列表
for index,val in enumerate(count): # 獲取新列表的索引(代表原列表中的數)和值(值代表這個數在原列表中的個數)
for i in range(val): # 將數寫入原列表中
li.append(index)
桶排序
#桶排序
def bucket_sort(li,n=100,max_num=10000):
buckets = [[] for i in range(n)] # 先創建n個數的桶
# 遍歷值,將每個數插入到桶中
for val in li:
i = min(val // (max_num//n),n - 1 ) # max_num/n代表每個桶中有多少個元素 i代表應該房放入哪個桶中 當超過最大桶的索引時,放入最大桶中
buckets[i].append(val) # 將元素放入目標桶中
# 給元素進行排序 用冒泡排序
for j in range(len(buckets[i])-1,0,-1): # 從最後一個元素,一直遍歷到第二個元素,j代表冒泡索引
if buckets[i][j] < buckets[i][j-1]: # 沒有找到位置,和前面的元素替換值
buckets[i][j],buckets[i][j-1] = buckets[i][j-1],buckets[i][j]
else: # 該元素找到位置後就退出
break
# 將所有排序完的元素合併
sorted_li = []
for buc in buckets:
sorted_li.extend(buc)
return sorted_li
桶排序如果先將數據放入桶中,再使用快排,速度非常快
def buckets_sort2(li,n=100,max_val=10000): # n代表有n個桶,max_val爲最大數字
buckets = [[] for _ in range(n)]
for val in li: # 將每個數字遍歷出來
i = min(val // (max_val // n),n-1) #確定數字在哪個桶裏面
buckets[i].append(val)
#給每個桶排序
for bucket in buckets:
_quick_sort(bucket) #這是一個快排函數,見上面快速排序
#將元素整合
result_list = []
for bucket in buckets:
result_list.extend(bucket)
return result_list
測試函數:
import time
#用於統計函數時間的裝飾器
def calTime(func):
def inner(*args,**kwargs):
t1 = time.time()
result = func(*args,**kwargs)
t2 = time.time()
print("%s running time:%.8s secs"%(func.__name__,t2-t1))
return result
return inner
#將時間裝飾器,裝飾到函數上,在函數名上一行開頭使用@calTime
#用於產生隨機數
import random
import copy
SORT_NUM = 100000
li = [random.randint(0,10000) for i in range(SORT_NUM)]
list1 = copy.deepcopy(li)
list2 = copy.deepcopy(li)
list3 = copy.deepcopy(li)
quick_sort(list1)
buckets_sort(list2)
buckets_sort2(list3)
SORT_NUM
爲1000個時,速度爲
quick_sort running time:0.002979 secs
buckets_sort running time:0.001994 secs
buckets_sort2 running time:0.001007 secs
SORT_NUM
爲100000個時(十萬),速度爲
quick_sort running time:0.400920 secs
buckets_sort running time:14.30812 secs
buckets_sort2 running time:0.293269 secs
基數排序
def radix_sort(li):
max_num = max(li)
it = 0
while 10 ** it <= max_num:
buckets = [[] for _ in range(10)]
for val in li:
digit = (val // 10 ** it) % 10 #從個位開始,十位,百位依次排序
buckets[digit].append(val)
li.clear()
# 將每次排序完,將其依次放入數組中
for buc in buckets:
li.extend(buc)
it += 1 #排完個位,排十位