冒泡排序
這個算法的名字由來是因爲越小的元素會經由交換慢慢“浮”到數列的頂端。
算法過程:
進行N-1趟操作
每一趟,都是不斷的比較相鄰的元素,那麼一趟下來,就會將最大的移到排好順序的最後面的位置。
代碼實現:
def bubbleSort(array):
'''
冒泡排序
'''
for i in range(len(array)-1,-1,-1):
for j in range(i):
if array[j] > array[j+1]:
array[j],array[j+1] = array[j+1],array[j]
return array
最佳情況:T(n) = O(n) 最壞情況:T(n) = O(n2) 平均情況:T(n) = O(n2)
選擇排序
選擇排序是最穩定的排序了,不管什麼情況都是O(n^2),唯一的優點可能是in-place,想法非常簡單,進行n-1趟排序,每次從沒排好序的部分選一個最小的元素放到已經排好序的序列的末尾。
代碼實現:
def selectSort(array):
'''
選擇排序
'''
for i in range(len(array)-1):
maxindex = i
for j in range(i+1,len(array)):
if array[maxindex] > array[j]:
maxindex = j
array[i],array[maxindex] = array[maxindex],array[i]
return array
最佳情況:T(n) = O(n2) 最差情況:T(n) = O(n2) 平均情況:T(n) = O(n2)
插入排序
原理:每次將沒有排好序列中的第一個插入到前面已經排好序列當中。
代碼實現:
def insertion_sort(array):
#插入排序
for i in range(1,len(array)):
cur = array[i]
for j in range(i-1,-1,-1):
if array[j] > cur:
array[j+1] = array[j]
else:
j += 1
break
array[j] = cur
return array
最佳情況:T(n) = O(n) 最壞情況:T(n) = O(n2) 平均情況:T(n) = O(n2)
希爾排序
第一個突破O(n^2)的算法,是插入排序的改進版。它與插入排序不同的是它會優先比較距離較遠的元素。又被稱爲縮小增量排序。它的核心在於間隔序列的設定,既可以提前設置好間隔,也可以動態的定義間隔。
算法流程:
選擇一個增量序列t1,t2……tk
按增量序列的個數K,對序列進行k趟插入排序
第i趟排序,以ti爲間隔構造若干的序列,對這些序列分別進行插入排序。
代碼如下:
def shellSort(array):
'''
希爾排序
'''
#構造增量序列 也可以動態構造
l = [5,2,1]
length = len(array)
for i in l:
for j in range(i):
temp = list(range(j,length,i))
#進行插入排序
for x in range(1,len(temp)):
cur = array[temp[x]]
for y in range(x-1,-1,-1):
if array[temp[y]] > cur:
array[temp[y+1]] = array[temp[y]]
else:
y = y + 1
break
array[temp[y]] = cur
return array
最佳情況:T(n) = O(nlog2 n) 最壞情況:T(n) = O(nlog2 n) 平均情況:T(n) =O(nlog n)
歸併排序
歸併排序比選擇排序性能要好很多,最差情況也是O(nlogn),是使用了分治思想,2-分路,遞歸
代碼如下:
def MergeSort(array):
if len(array) == 0:
return []
elif len(array) == 1:
return array
left = MergeSort(array[0:len(array)//2])
right = MergeSort(array[len(array)//2:])
result = []
i = 0
j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
while i < len(left):
result.append(left[i])
i += 1
while j < len(right):
result.append(right[j])
j += 1
return result
最佳情況:T(n) = O(n) 最差情況:T(n) = O(nlogn) 平均情況:T(n) = O(nlogn)
快速排序
找到一個基準,將比他大的排到它的右側,比他小的排在左側,然後分別對兩邊在進行快速排序,一個遞歸的過程。
代碼實現:
import random
def quickSort(array):
if len(array) <= 1:
return array
#隨機生成一個基準
index = random.randint(0,len(array)-1)
basic = array[index]
left = []
right = []
for i in range(0,index):
if array[i] < basic:
left.append(array[i])
else:
right.append(array[i])
for i in range(index+1,len(array)):
if array[i] < basic:
left.append(array[i])
else:
right.append(array[i])
left = quickSort(left)
right = quickSort(right)
result = []
result.extend(left)
result.append(basic)
result.extend(right)
return result
最佳情況:T(n) = O(nlogn) 最差情況:T(n) = O(n2) 平均情況:T(n) = O(nlogn)
堆排序
堆排序是完全二叉樹,分爲大頂堆(上面最大),小頂堆
完全二叉樹:除了最後一層外,,每一層都被完全填充
滿二叉樹:除了葉子結點之外的每一個結點都有兩個孩子,每一層(當然包含最後一層)都被完全填充。
完滿二叉樹:除了葉子結點之外的每一個結點都有兩個孩子結點。
首先構建成大頂堆?
首先從最後的一個給葉子節點開始遍歷,這個葉子節點如果比左右子節點小的話,就要進行‘’下沉‘’操作(這個過程遞歸的)
重複上述操作直至根節點,這樣就構成了一個大頂堆
排序過程:
首先拿出根節點來,然後將最後一個葉子節點重新作爲一個新的根節點,在對新的根節點進行“下沉”操作。
在堆當中,除了‘下沉’操作之外,與之對應的是‘上浮’操作,當插入一個新的節點中使用得到。
Left = 2 * index + 1
Right = 2 * index + 2
最後一個非葉子節點就是**(len(array) – 1)//2**
代碼實現:
def Initheapmin(array):
#完全二叉樹
#小頂堆
l = len(array)
for i in range((l - 1) // 2,-1,-1):
adjust_heap(array,i)
return array
def adjust_heap(array,i):
l = len(array)
left = 2 * i +1
right = 2 * i + 2
while left < l and right < l:
if array[i] <= array[left] and array[i] <= array[right]:
#滿足條件的時候
break
elif array[i] <= array[left]:
#左節點滿足,右節點不滿足
array[i],array[right] = array[right],array[i]
i = right
elif array[i] <= array[right]:
array[i],array[left] = array[left],array[i]
else:
array[i],array[left] = array[left],array[i]
i = left
left = 2 * i +1
right = 2 * i + 2
if left < l and right >= l:
if array[i] > array[left]:
array[i],array[left] = array[left],array[i]
def heap_sort(array):
array = Initheapmin(array)
result = []
while len(array)>0:
result.append(array[0])
array[0] = array[-1]
del array[-1]
adjust_heap(array,0)
return result
最佳情況:T(n) = O(nlogn) 最差情況:T(n) = O(nlogn) 平均情況:T(n) = O(nlogn)
計數排序
計數排序是一個線性時間內的排序,但是它要還使用一個額外的數組進行統計,它·排序的數組只能是確定範圍的,並且它是穩定排序。
過程:
- 找出數據中最大的和最小的數據構建額外開闢的數組空間C內
- 統計
- 反向生成目標數組
代碼:
def counting_sort(array):
minx = min(array)
maxx = max(array)
c = [0] * (maxx - minx + 1)
for i in array:
c[i - minx] += 1
index = 0
for i in range(len(c)):
for j in range(c[i]):
array[index] = i + minx
index += 1
return array
最佳情況:T(n) = O(n+k) 最差情況:T(n) = O(n+k) 平均情況:T(n) = O(n+k)。
桶排序
桶排序是計數排序的升級版,他利用了函數的映射關係,排序的高效與否在於這個映射關係的選擇。
假定數據服從均勻分佈。
代碼實現:
import math
class ListNode():
def __init__(self,x):
self.val = x
self.next = None
def bucket_sort(array):
minx = min(array)
maxn = max(array)
#構建桶
bucketSize = 5
bucketList = []
for i in range(bucketSize):
head = ListNode(0)
bucketList.append(head)
#利用映射函數將數據添加到各個桶中
group = math.ceil((maxn - minx) // bucketSize)
for data in array:
index = (data - minx) // group
if index >= bucketSize:
index -= 1
bucketList[index].val += 1
if bucketList[index].next == None:
bucketList[index].next = ListNode(data)
else:
pre = bucketList[index]
cur = bucketList[index].next
flag = True
while cur != None:
if data > cur.val:
pre = cur
if cur.next:
cur= cur.next
else:
break
else:
pre.next = ListNode(data)
pre.next.next = cur
flag = False
break
if flag:
cur.next = ListNode(data)
index = 0
for i in range(bucketSize):
cur = bucketList[i].next
while cur:
array[index] = cur.val
cur = cur.next
index += 1
return array
最佳情況:T(n) = O(n+k) 最差情況:T(n) = O(n+k) 平均情況:T(n) = O(n2)
基數排序
基數排序對每一位進行排序,首先取得最大數的位數,然後從最後一位開始,使用計數排序進行排序,之後進行收集,然後在對高位進行上述操作。
代碼實現:
def radix_sort(array):
maxn = max(array)
length = len(str(maxn))
for i in range(1,length+1):
print(i)
array = counting_sort(array,i)
return array
def counting_sort(array,l):
c = []
for i in range(10):
c.append([])
for i in array:
if len(str(i)) >= l:
c[int(str(i)[-l])].append(i)
else:
c[0].append(i)
index = 0
for i in range(10):
for j in c[i]:
array[index] = j
index += 1
return array
T(n) = O(n * k) 最差情況:T(n) = O(n * k) 平均情況:T(n) = O(n * k)
最後三種排序算法都利用了桶的概念,但對桶的使用方法上有明顯差異:
• 基數排序:根據鍵值的每位數字來分配桶
• 計數排序:每個桶只存儲單一鍵值
• 桶排序:每個桶存儲一定範圍的數值