圖片來源:十大經典排序算法(動圖演示)
圖片來源:十大經典排序算法(動圖演示)
1 Sort
1.1 冒泡排序
圖片來自於:https://www.bilibili.com/video/av53583801/?p=35
動圖來源:十大經典排序算法(動圖演示)
思路(升序):
n-1 趟,每一趟確定一個數的最終位置,
每一趟兩兩元素(未確定最終位置)比較,大的往後放
nums = [3,5,6,7,8,9,2,1,4]
def bubble(nums):
n = len(nums)
for i in range(n-1): # 排序 n-1 趟,每一趟最少排好一個數
count = 0 # 記錄交換的次數
for j in range(n-1-i): # 兩個元素比較的次數
if nums[j] > nums[j+1]: # 這裏的符號改變一下就是逆序了
nums[j],nums[j+1] = nums[j+1],nums[j]
count+=1
if count==0: # 某一趟中,沒有元素交換,說明排序結束了,退出
break
return nums
print(bubble(nums))
output
[1, 2, 3, 4, 5, 6, 7, 8, 9]
注意:沒有 count 的統計和判斷,那麼最好最壞的複雜度都是 ,有了以後最好的是 (原來的數組就有序),最壞的 (逆序)!算法是穩定的
1.2 選擇排序
動圖來源:十大經典排序算法(動圖演示)
思路(升序):將數組分爲兩部分,取後面部分的最小值放在前面!每一趟確認前面部分的一個值!
nums = [3,5,6,7,8,9,2,1,4]
def select_sort(nums):
n = len(nums)
for i in range(n-1):
min_index = i # 初始最小值下標爲 i
for j in range(i+1,n): # 遍歷i+1到n,找最小值的下標
if nums[j] < nums[min_index]:
min_index = j
nums[i],nums[min_index] = nums[min_index],nums[i] # 交換初始化最小值下標和比較後的最小值下標
return nums
print(select_sort(nums))
分析:最壞最好都是 ,不穩定的,如果算法改爲,把從前面部分選最大值放在後面部分,發現相同元素的位置調換了
eg 3(1) 3(2) 1 2
第一趟:3(2) 1 2 | 3(1)
第二趟:1 2 | 3(2) 3(1)
第三趟:1 | 2 3(2) 3(1)
可以看出算法是不穩定的
1.3 插入排序
動圖來源:十大經典排序算法(動圖演示)
同選擇排序,也是將數組分爲兩個部分,從後面部分選擇元素插入到第一部分中,插入的過程是逐個比較,比前部分小就交換位置!
nums = [3,5,6,7,8,9,2,1,4]
def insert_sort(nums):
n = len(nums)
for i in range(1,n):
for j in reversed(range(1,i+1)): # 反向遍歷 i 到 1
if nums[j] < nums[j-1]: # 和前一個元素比較大小
nums[j],nums[j-1] = nums[j-1], nums[j] #小的話交換位置
else: # 大的話直接退出,保證了最優爲 O(n)
break
return nums
print(insert_sort(nums))
最好 ,就是有序的時候都 break 了,最壞的是 (兩層循環),平均的是 ,是穩定的!
1.4 希爾排序
動圖來源:十大經典排序算法(動圖演示)
插入排序的改進版,將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序(直接插入排序的步長變爲了gap,而不是1了)!不同的分割策略會影響算法的時間複雜度,gap 爲1的時候就是插入排序,eg 4,2,1。
nums = [3,5,6,7,8,9,2,1,4]
def shell_sort(nums):
n = len(nums)
gap = n//2
while(gap>0):
for i in range(gap,n):
for j in reversed(range(1,i+1,gap)): # 反向遍歷 i 到 1,步長變爲了 gap
if nums[j] < nums[j-1]: # 和前一個元素比較大小
nums[j],nums[j-1] = nums[j-1], nums[j] #小的話交換位置
else: # 大的話直接退出,保證了最優爲 O(n)
break
gap //=2 # gap 的策略,這裏是 1/2
return nums
print(shell_sort(nums))
1.5 快排
介紹可以參考 python】Leetcode(Data Structure / Algorithm) 中的 215. 數組中的第K個最大元素(快排)
partion 函數,把小於 base 的放在左邊,把大於base 的放在右邊,然後遞歸下去!
nums = [3,5,6,7,8,9,2,1,4]
def quick_sort(nums,start,end):
if start<end:
base = partion(nums,start,end)
quick_sort(nums,start,base-1)
quick_sort(nums,base+1,end)
return nums
def partion(nums,start,end):
l = start
r = end
base = nums[l]
while(l<r):
while(l<r and nums[r]>=base):
r-=1
nums[l] = nums[r]
while(l<r and nums[l]<=base):
l+=1
nums[r] = nums[l]
nums[l] = base
return l
print(quick_sort(nums,0,len(nums)-1))
1.6 歸併排序
穩定的,最好最壞的平均的時間複雜度都是 ,但是會生成同樣大小的數組,思想是,一直分下去(eg 2,4,8……),然後排序後合起來!排序的過程用到了雙指針,一個指向二分左部分的,一個指向二分右部分的,比較大小移動指針來合併成一個排序的新數組!
圖片來自於:https://www.bilibili.com/video/av53583801/?p=46
圖片來自於:https://www.bilibili.com/video/av53583801/?p=35
nums = [3,5,6,7,8,9,2,1,4]
def merge_sort(nums):
if len(nums)<=1: # 遞歸終止條件
return nums
mid = len(nums)//2
l_list = merge_sort(nums[:mid]) # 分成左右兩部分
r_list = merge_sort(nums[mid:]) # 分成左右兩部分
result = [] # 存放排序的結果
l,r = 0,0 # 用兩個指針對左右兩部分進行排序
while(l<len(l_list) and r<len(r_list)): # 左右有一個到了盡頭的時候退出
if l_list[l]<=r_list[r]:
result.append(l_list[l])
l+=1
else:
result.append(r_list[r])
r+=1
result+=l_list[l:] # 把剩下的掛在result的後面
result+=r_list[r:] # 把剩下的掛在result的後面
return result # 返回排序後的結果
print(merge_sort(nums))
2 Search
二分查找
遞歸版本
nums = [1,2,3,4,5,6,7,8,9]
def binary_search(nums,item):
if len(nums)>0: # 這個條件很關鍵
mid = len(nums)//2
if item == nums[mid]:
return True
elif item < nums[mid]:
return binary_search(nums[:mid],item)
elif item > nums[mid]:
return binary_search(nums[mid+1:], item)
return False
print(binary_search(nums,5))
print(binary_search(nums,10))
output
True
False
非遞歸版本
nums = [1,2,3,4,5,6,7,8,9]
def binary_search(nums,item):
l = 0
r = len(nums)-1
while(l<=r):
mid = (l+r) // 2
if item == nums[mid]:
return True
elif item < nums[mid]:
r = mid - 1
elif item > nums[mid]:
l = mid + 1
return False
print(binary_search(nums,5))
print(binary_search(nums,10))
output
True
False
最優
最壞