本博客來自於牛客網左神初級班的筆記整理,個人將java改寫爲python
1、冒泡排序
每次循環將最大的沉底。時間複雜度爲O(n^2),空間複雜度爲O(1)
def swap(nums, i, j):
temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
#冒泡排序
def bubbleSort(nums):
for i in range(len(nums)):
for j in range(len(nums)-i-1):
if nums[j]>nums[j+1]:
swap(nums,j,j+1)
if __name__ == "__main__":
sort_list = list(map(int, input().split()))
bubbleSort(sort_list)
print(sort_list)
2、插入排序
每次選擇下一個數插入到已經排好序的列表中。時間複雜度爲O(n^2),空間複雜度爲O(1)
def swap(nums, i, j):
temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
#插入排序
def insertSort(nums):
for i in range(len(nums)):
for j in range(i-1,-1,-1):
if nums[j]>nums[j+1]:
swap(nums,j,j+1)
if __name__ == "__main__":
sort_list = list(map(int, input().split()))
insertSort(sort_list)
print(sort_list)
3、選擇排序
在未排序列中找到最大(最小)元素,然後放到已排序列的末尾。時間複雜度爲O(n^2),空間複雜度爲O(1)
def swap(nums, i, j):
temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
#選擇排序
def selectSort(nums):
for i in range(len(nums)):
minIndex = i
for j in range(i+1,len(nums)):
minIndex = j if nums[j] < nums[minIndex] else minIndex
swap(nums,i,minIndex)
if __name__ == "__main__":
sort_list = list(map(int, input().split()))
selectSort(sort_list)
print(sort_list)
4、歸併排序
先考慮合併兩個有序數組,兩個數組初始位置分別設置兩個指針,先取較小的那一個,然後對應指針向後移,直到一個數組爲空爲止,然後將另一個數組的剩餘部分複製過去。
再考慮利用二分法,將數組不停拆分,直至拆分爲1,然後利用上述步驟合併成有序數組爲止。好處是對於有序數組內的數字無需再比較,故節約了時間。這是一個分治法的典型應用。
歸併排序時間複雜度爲O(nlogn),空間複雜度爲O(n)(help數組所需)
def mergeSort(nums):
if len(nums)<2:
return nums
mid = len(nums)//2
left = mergeSort(nums[:mid])
right = mergeSort(nums[mid:])
return merge(left, right)
def merge(left, right):
help = []
l,r = 0,0
while l<len(left) and r<len(right):
if left[l] < right[r]:
help.append(left[l])
l += 1
else:
help.append(right[r])
r += 1
help += left[l:]
help += right[r:]
return help
if __name__ == "__main__":
sort_list = list(map(int, input().split()))
print(mergeSort(sort_list))
注:此寫法並未改變sort_list數組本身。
5、快速排序
選取一個數,大於這個數的放右邊,小於這個數的放左邊,然後從分好的左右區間分別重複此步驟,直至各區間只有一個數爲止。主要思想是兩個指針,一個從後往前,遇到小於key值的停止,一個從前往後,遇到大於Key值的時候停止,然後將兩數交換,最後將key值與指針重合的地方交換。此方法一般的時間複雜度是O(nlogn),但當數組本身有序時,複雜度退化爲O(n^2)。因此當採用隨機選取區間中的數時,變成O(n^2)的概率就變成了一個期望值,比單純選取最後一個數要好。空間複雜度爲O(logn)
def swap(nums, i, j):
temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
#快速排序
def quickSort(nums):
qSort(nums,0,len(nums)-1)
def qSort(nums, left, right):
if left > right:
return nums
key = nums[left]
l = left
r = right
while l<r:
while nums[r] >= key and l<r:
r -= 1
while nums[l] <= key and l<r:
l += 1
swap(nums,l,r)
swap(nums,left,l)
qSort(nums,left,l-1)
qSort(nums,r+1,right)
if __name__ == "__main__":
sort_list = list(map(int, input().split()))
quickSort(sort_list)
print(sort_list)
這裏是普通快排,並不是隨機快排
7、堆排序
堆本質上還是一維數組,只不過是用二叉樹的思想來模擬,形式上表現爲完全二叉樹,具體公式如下:
大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序算法在https://www.cnblogs.com/chengxiao/p/6129630.html中講解的非常詳細,此處不再敘述
主要三個步驟爲:構建初始堆、堆排序、最大堆調整
def swap(nums, i, j):
temp = nums[i]
nums[i] = nums[j]
nums[j] = temp
#堆排序
def heapSort(nums) :
n = len(nums)
first = int(n/2-1) #最後一個非葉子節點
for start in range(first,-1,-1) : #構造大根堆
max_heapify(nums,start,n-1)
for end in range(n-1,0,-1): #堆排,將大根堆轉換成有序數組
swap(nums,0,end)
max_heapify(nums,0,end-1)
return nums
#最大堆調整:將堆的末端子節點作調整,使得子節點永遠小於父節點
#start爲當前需要調整最大堆的位置,end爲調整邊界
def max_heapify(ary,start,end):
root = start
while True :
child = root*2 +1 #調整節點的子節點
if child > end:
break
if child+1 <= end and ary[child] < ary[child+1]:
child = child+1 #取較大的子節點
if ary[root] < ary[child]: #較大的子節點成爲父節點
swap(ary,root,child) #交換
root = child
else:
break
if __name__ == "__main__":
sort_list = list(map(int, input().split()))
heapSort(sort_list)
print(sort_list)
================================================================================================
以上都是基於比較的排序。還有非基於比較的排序,如桶排序、計數排序、基數排序等。這種排序與被排序的數據樣本很有關係,所以在實際中並不常用。時間複雜度與空間複雜度爲O(N),穩定排序。
應用題:相鄰最大差值。牛客網題目鏈接:https://www.nowcoder.com/practice/376ede61d9654bc09dd7d9fa9a4b0bcd?tpId=49&tqId=29366&rp=5&ru=/ta/2016test&qru=/ta/2016test/question-ranking
此題目借鑑了桶排序和鴿籠原理的思想,先找出最小數和最大數之間的範圍,然後在有N個數的情況下,均分成N+1個桶,根據鴿籠原理,必有空桶,因此最大差值爲空桶前後桶之間後桶的最小值減去前桶的最大值。
class MaxDivision:
def findMaxDivision(self, A, n):
# write code here
minn = min(A)
maxx = max(A)
# 生成桶
res = [0 for i in range(maxx - minn + 1)]
# 填桶
for i in range(n):
res[A[i] - minn] += 1
count = 0
num = -0x3f3f3f3f
for i in range(len(res)):
if res[i] == 0:
# 如果說當前的桶爲空,則記錄下來連續的空桶數
count += 1
else:
if num < count:
num = count
count = 0
# 爲何加1?舉例如下:最大值爲9,最小值爲3,中間有5個空桶,但差值應爲6
return num + 1
工程中的綜合排序算法:當數組很短時(如小於60)直接使用插入排序,當數組很長時,對於基礎類型(int,double,float)等,一般使用快排,因爲基礎類型無需穩定性,對於自定義類型,需要穩定性的,則使用歸併。