技術博客: github.com/yongxinz/te…html
本文用 Python 實現了插入排序、希爾排序、冒泡排序、快速排序、直接選擇排序、堆排序、歸併排序。python
先總體看一下各個算法之間的對比,而後再進行詳細介紹:git
排序算法 | 平均時間複雜度 | 最好狀況 | 最壞狀況 | 空間複雜度 | 排序方式 | 穩定性 |
---|---|---|---|---|---|---|
插入排序 | O(n²) | O(n) | O(n²) | O(1) | In-place | 穩定 |
冒泡排序 | O(n²) | O(n) | O(n²) | O(1) | In-place | 穩定 |
選擇排序 | O(n²) | O(n²) | O(n²) | O(1) | In-place | 不穩定 |
快速排序 | O(n log n) | O(n log n) | O(n²) | O(log n) | In-place | 不穩定 |
希爾排序 | O(n log n) | O(n log n) | O(n log n) | O(1) | In-place | 不穩定 |
堆排序 | O(n log n) | O(n log n) | O(n log n) | O(1) | In-place | 不穩定 |
歸併排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | Out-place | 穩定 |
n:數據規模github
In-place:佔用常數內存,不佔用額外內存算法
Out-place:佔用額外內存shell
穩定性:排序後 2 個相等鍵值的順序和排序以前它們的順序相同數組
描述數據結構
時間複雜度爲 O(n^2),是穩定的排序方法。app
插入排序的基本操做就是將一個數據插入到已經排好序的有序數據中,從而獲得一個新的、個數加一的有序數據,算法適用於少許數據的排序。ide
插入算法把要排序的數組分紅兩部分:第一部分包含了這個數組的全部元素,但將最後一個元素除外(讓數組多一個空間纔有插入的位置),而第二部分就只包含這一個元素(即待插入元素)。在第一部分排序完成後,再將這個最後元素插入到已排好序的第一部分中。
代碼實現
# -*- coding: UTF-8 -*-
def insert_sort(lists):
# 插入排序 時間複雜度爲 O(n^2)
count = len(lists)
for i in range(1, count):
key = lists[i]
j = i - 1
while j >= 0:
if lists[j] > key:
lists[j + 1] = lists[j]
lists[j] = key
j -= 1
return lists
if __name__ == "__main__":
test = [2, 5, 4, 6, 7, 3, 2]
print(insert_sort(test))
複製代碼
描述
時間複雜度爲 O(n log n),是不穩定的排序方法。
希爾排序(Shell Sort)是插入排序的一種,也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。
該方法因 DL.Shell 於 1959 年提出而得名。 希爾排序是把記錄按下標的必定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減小,每組包含的關鍵詞愈來愈多,當增量減至 1 時,整個文件恰被分紅一組,算法便終止。
代碼實現
# -*- coding: UTF-8 -*-
def shell_sort(lists):
# 希爾排序 時間複雜度是 O(n log n)
count = len(lists)
step = 2
group = int(count / step)
while group > 0:
for i in range(0, group):
j = i + group
while j < count:
k = j - group
key = lists[j]
while k >= 0:
if lists[k] > key:
lists[k + group] = lists[k]
lists[k] = key
k -= group
j += group
group = int(group / step)
return lists
if __name__ == "__main__":
test = [2, 5, 4, 6, 7, 3, 2]
print(shell_sort(test))
複製代碼
描述
時間複雜度是 O(n²), 是穩定排序算法。
它重複地走訪過要排序的數列,一次比較兩個元素,若是他們的順序錯誤就把他們交換過來。走訪數列的工做是重複地進行直到沒有再須要交換,也就是說該數列已經排序完成。
代碼實現
# -*- coding: UTF-8 -*-
def bubble_sort(lists):
# 冒泡排序 時間複雜度是 O(n²)
count = len(lists)
for i in range(0, count - 1):
for j in range(0, count - i - 1):
if lists[j] > lists[j + 1]:
lists[j], lists[j + 1] = lists[j + 1], lists[j]
return lists
if __name__ == "__main__":
test = [2, 5, 4, 6, 7, 3, 2]
print(bubble_sort(test))
複製代碼
描述
快速排序的時間複雜度是 O(n log n), 是不穩定排序算法。
經過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的全部數據都比另一部分的全部數據都要小,而後再按此方法對這兩部分數據分別進行快速排序,整個排序過程能夠遞歸進行,以此達到整個數據變成有序序列。
代碼實現
# -*- coding: UTF-8 -*-
def quick_sort(lists, left, right):
# 快速排序 時間複雜度是 O(n log n)
if left >= right:
return lists
key = lists[left]
low = left
high = right
while left < right:
while left < right and lists[right] >= key:
right -= 1
lists[left] = lists[right]
while left < right and lists[left] <= key:
left += 1
lists[right] = lists[left]
lists[right] = key
quick_sort(lists, low, left - 1)
quick_sort(lists, left + 1, high)
return lists
if __name__ == "__main__":
test = [2, 5, 4, 6, 7, 3, 2]
print(quick_sort(test, 0, len(test) - 1))
複製代碼
描述
選擇排序的時間複雜度是 O(n²), 是不穩定排序算法。
基本思想:第 1 趟,在待排序記錄 r1 ~ r[n] 中選出最小的記錄,將它與 r1 交換;第 2 趟,在待排序記錄 r2 ~ r[n] 中選出最小的記錄,將它與 r2 交換;以此類推,第 i 趟在待排序記錄 r[i] ~ r[n] 中選出最小的記錄,將它與 r[i] 交換,使有序序列不斷增加直到所有排序完畢。
代碼實現
# -*- coding: UTF-8 -*-
def select_sort(lists):
# 選擇排序 時間複雜度是 O(n²)
count = len(lists)
for i in range(0, count):
min = i
for j in range(i + 1, count):
if lists[min] > lists[j]:
min = j
lists[min], lists[i] = lists[i], lists[min]
return lists
if __name__ == "__main__":
test = [2, 5, 4, 6, 7, 3, 2]
print(select_sort(test))
複製代碼
描述
堆排序的時間複雜度是 O(n log n), 是不穩定排序算法。
堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。能夠利用數組的特色快速定位指定索引的元素。堆分爲大根堆和小根堆,是徹底二叉樹。大根堆的要求是每一個節點的值都不大於其父節點的值,即 A[PARENT[i]] >= A[i]
。在數組的非降序排序中,須要使用的就是大根堆,由於根據大根堆的要求可知,最大的值必定在堆頂。
代碼實現
# -*- coding: UTF-8 -*-
from collections import deque
def swap_param(lists, i, j):
lists[i], lists[j] = lists[j], lists[i]
return lists
def heap_adjust(lists, start, end):
temp = lists[start]
i = start
j = 2 * i
while j <= end:
if (j < end) and (lists[j] < lists[j + 1]):
j += 1
if temp < lists[j]:
lists[i] = lists[j]
i = j
j = 2 * i
else:
break
lists[i] = temp
def heap_sort(lists):
length = len(lists) - 1
first_sort_count = length / 2
for i in range(first_sort_count):
heap_adjust(lists, first_sort_count - i, length)
for i in range(length - 1):
lists = swap_param(lists, 1, length - i)
heap_adjust(lists, 1, length - i - 1)
return [lists[i] for i in range(1, len(lists))]
def main():
lists = deque([50, 16, 30, 10, 60, 90, 2, 80, 70])
lists.appendleft(0)
print heap_sort(lists)
if __name__ == '__main__':
main()
複製代碼
描述
時間複雜度是 O(n log n), 是穩定排序算法。
歸併排序是創建在歸併操做上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。將已有序的子序列合併,獲得徹底有序的序列;即先使每一個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併。
歸併過程爲:比較a[i]和a[j]的大小,若a[i]≤a[j],則將第一個有序表中的元素a[i]複製到r[k]中,並令i和k分別加上1;不然將第二個有序表中的元素a[j]複製到r[k]中,並令j和k分別加上1,如此循環下去,直到其中一個有序表取完,而後再將另外一個有序表中剩餘的元素複製到r中從下標k到下標t的單元。歸併排序的算法咱們一般用遞歸實現,先把待排序區間[s,t]以中點二分,接着把左邊子區間排序,再把右邊子區間排序,最後把左區間和右區間用一次歸併操做合併成有序的區間[s,t]。
代碼實現
# -*- coding: UTF-8 -*-
def merge_sort(lists):
# 歸併排序 時間複雜度是 O(n log n)
if len(lists) <= 1:
return lists
num = int(len(lists) / 2)
left_lists = merge_sort(lists[:num])
right_lists = merge_sort(lists[num:])
return merge(left_lists, right_lists)
def merge(left_lists, right_lists):
left, right = 0, 0
result = []
while left < len(left_lists) and right < len(right_lists):
if left_lists[left] < right_lists[right]:
result.append(left_lists[left])
left += 1
else:
result.append(right_lists[right])
right += 1
result += left_lists[left:]
result += right_lists[right:]
return result
if __name__ == "__main__":
test = [2, 5, 4, 6, 7, 3, 2]
print(merge_sort(test))
複製代碼
參考文檔: