定義:算法(Algorithm)是指解題方案的準確而完整的描述,是一系列解決問題的清晰指令,算法表明着用系統的方法描述解決問題的策略機制。也就是說,可以對必定規範的輸入,在有限時間內得到所要求的輸出。若是一個算法有缺陷,或不適合於某個問題,執行這個算法將不會解決這個問題。不一樣的算法可能用不一樣的時間、空間或效率來完成一樣的任務。一個算法的優劣能夠用空間複雜度與時間複雜度來衡量。python
一個算法應該具備如下五個重要的特徵:算法
算法設計的要求: shell
1.算法程序無語法錯誤;數組
2.算法程序對於合法的輸入產生知足要求的輸出; app
3.對於非法輸入可以產生知足規格的說明;dom
4.算法程序對於故意刁難的測試輸入都有知足要求的輸出結果。 函數
過後統計方法:主要是經過設計好的測試程序和數據,利用計算機計時器對不一樣算法編制的程序的運行時間進行比較,從而肯定算法效率的高低,但這種方法有很大缺陷,通常不予採納。性能
事前分析估算方法:在計算機程序編制前,依據統計方法對算法進行估算。測試
一個用高級語言編寫的程序在計算機上運行時所消耗的時間取決於如下因素:優化
定義:在進行算法分析時,語句總的執行次數T(n)是關於問題規模n的函數,進而分析T(n)隨n的變化狀況並肯定T(n)的數量級。算法的時間複雜度,也就是算法的時間量度,記做:T(n}=0(f(n))。它表示隨問題規模n的增大,算法執行時間的埔長率和 f(n)的埔長率相同,稱做算法的漸近時間複雜度,簡稱爲時間複雜度。其中f( n)是問題規橫n的某個函數。
根據定義,求解算法的時間複雜度的具體步驟是:
⑴ 找出算法中的基本語句;
算法中執行次數最多的那條語句就是基本語句,一般是最內層循環的循環體。
⑵ 計算基本語句的執行次數的數量級;
只需計算基本語句執行次數的數量級,這就意味着只要保證基本語句執行次數的函數中的最高次冪正確便可,能夠忽略全部低次冪和最高次冪的係數。這樣可以簡化算法分析,而且使注意力集中在最重要的一點上:增加率。
⑶ 用大Ο記號表示算法的時間性能。
將基本語句執行次數的數量級放入大Ο記號中。
如何推導大o階呢?下面是基本的推導方法:
1.用常數1取代運行時間中的全部加法常數。
2.在修改後的運行次數函數中,只保留最髙階項。
3.若是最高階項存在且不是1,則去除與這個項相乘的常數。
簡單的說,就是保留求出次數的最高次冪,而且把係數去掉。 如T(n)=n2+n+1 =O(n2)
######複雜度O(1) print("this is wd") ######複雜度O(n) for i in range(n): print(i) ######複雜度O(n2) for i in range(n): for j in range(n): print(j) ######複雜度O(n3) for i in range(n): for j in range(n): for k in range(n): print('wd') ######複雜度O(log2n) while n > 1: print(n) n = n // 2
常見的複雜度按效率排序:O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(2nlogn)<O(n2)
空間複雜度(Space Complexity)是對一個算法在運行過程當中臨時佔用存儲空間大小的量度。一個算法在計算機存儲器上所佔用的存儲空間,包括存儲算法自己所佔用的存儲空間,算法的輸入輸出數據所佔用的存儲空間和算法在運行過程當中臨時佔用的存儲空間這三個方面。算法的輸入輸出數據所佔用的存儲空間是由要解決的問題決定的,是經過參數表由調用函數傳遞而來的,它不隨本算法的不一樣而改變。存儲算法自己所佔用的存儲空間與算法書寫的長短成正比,要壓縮這方面的存儲空間,就必須編寫出較短的算法。算法在運行過程當中臨時佔用的存儲空間隨算法的不一樣而異,有的算法只須要佔用少許的臨時工做單元,並且不隨問題規模的大小而改變,這種算法是節省存儲的算法;有的算法須要佔用的臨時工做單元數與解決問題的規模n有關,它隨着n的增大而增大,當n較大時,將佔用較多的存儲單元。
如當一個算法的空間複雜度爲一個常量,即不隨被處理數據量n的大小而改變時,可表示爲O(1);當一個算法的空間複雜度與以2爲底的n的對數成正比時,可表示爲0(log2n);當一個算法的空間複雜度與n成線性比例關係時,可表示爲0(n).若形參爲數組,則只須要爲它分配一個存儲由實參傳送來的一個地址指針的空間,即一個機器字長空間;若形參爲引用方式,則也只須要爲其分配存儲一個地址的空間,用它來存儲對應實參變量的地址,以便由系統自動引用實參變量。
效率:O(n2)
原理:
demo:
def bubble_sort(data): """ 冒泡排序 :param data: :return: """ for i in range(len(data)-1): # 趟數 for j in range(len(data)-i-1): # 遍歷數據,依次交換 if data[j]>data[j+1]: # 當較大數在前面 data[j],data[j+1]=data[j+1],data[j] #交換兩個數的位置 if __name__=='__main__': import random data_list=list(range(30)) random.shuffle(data_list) print("pre:",data_list) bubble_sort(data_list) print("after:",data_list) #結果: #pre: [22, 11, 19, 16, 12, 18, 20, 28, 27, 4, 21, 10, 9, 7, 1, 6, 5, 29, 8, 0, 17, 26, 13, 14, 15, 24, 25, 23, 3, 2] #after: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
優化版本:當某一趟走完之後發現並無進行數據交換,那麼此時的數列已經排列好了,沒有必要在進行下去。例如:極端狀況下,數列原本已經排序好的,咱們只須要走一趟便可完成排序。
def bubble_sort(data): """ 冒泡排序優化版 :param data: :return: """ for i in range(len(data)-1): # 趟數 exchange=False # 交換標誌 for j in range(len(data)-i-1): # 遍歷數據,依次交換 if data[j]>data[j+1]: # 當較大數在前面 data[j],data[j+1]=data[j+1],data[j] # 交換兩個數的位置 exchange = True # 改變標誌 if not exchange: # 若是某一趟沒有進行交換,表明排序完成 break return i # 返回次數的趟數 if __name__=='__main__': data_list=list(range(30)) print("pre:",data_list) num =bubble_sort(data_list) print("after:",data_list,'趟數:',num+1) #結果: #pre: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] #after: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] 趟數: 1
效率:O(n2)
原理:
demo:
def select_sort(data): """ 選擇排序 :param data: 待排序的數據列表 :return: """ for i in range(len(data)-1): #趟數 min_index=i # 記錄i趟開始最小的數的索引,咱們從最左邊開始 for j in range(i+1,len(data)): # 每一次趟須要循環的次數 if data[j] < data[min_index]: # 當數列中的某一個數比開始的數要小時候,更新最小值索引位置 min_index=j data[i],data[min_index]=data[min_index],data[i] # 一趟走完,交換最小值的位置,第一趟最小 if __name__=='__main__': import random data_list=list(range(30)) random.shuffle(data_list) # 打亂列表數據 print("pre:",data_list) select_sort(data_list) print("after:",data_list) #結果: #pre: [20, 11, 22, 0, 18, 21, 14, 19, 7, 23, 27, 29, 24, 4, 17, 15, 5, 10, 26, 13, 25, 1, 8, 16, 3, 9, 2, 28, 12, 6] #after: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
效率:O(n2)
原理:
demo:
def insert_sort(data): """ 插入排序 :param data: 待排序的數據列表 :return: """ for i in range(1, len(data)): # 無序區域數據 tmp = data[i] # 第i次插入的基準數 for j in range(i, -1, -1): if tmp < data[j - 1]: # j爲當前位置,試探j-1位置 data[j] = data[j - 1] # 移動當前位置 else: # 位置肯定爲j break data[j] = tmp # 將當前位置數還原 if __name__=='__main__': import random data_list=list(range(30)) random.shuffle(data_list) # 打亂列表數據 print("pre:",data_list) insert_sort(data_list) print("after:",data_list) #結果: #pre: [7, 17, 10, 16, 23, 24, 13, 11, 2, 5, 15, 29, 27, 18, 4, 19, 1, 9, 3, 21, 0, 14, 12, 25, 22, 28, 20, 6, 26, 8] #after: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
效率:平均O(nlogn)
原理:
demo:
#!/usr/bin/env python3 #_*_ coding:utf-8 _*_ #Author:wd def quick_sort(data,left,right): """ 快速排序 :param data: 待排序的數據列表 :param left: 基準數左邊元素的索引 :param right: 基準數右邊元素的索引 :return: """ if left < right: mid = partition(data,left,right) # 分區操做,mid表明基數所在的索引 quick_sort(data,left,mid-1) # 對基準數前面進行排序 quick_sort(data,mid+1,right) # 對基準數後面進行排序 def partition(data,left,right): tmp=data[left] # 隨機選擇的基準數,從最左邊開始選 while left < right: while left < right and data[right] >= tmp: # 右邊的數比基準數大 right-=1 # 保留該數,而後索引指針往左移動 data[left]=data[right] # 不然此時右邊數比基數小,則將該數放到基準位置 while left < right and data[left] <= tmp: # 右邊的數比基準數小 left+=1 # 此時保持該數位置不動,索引指針往前移動 data[right]=data[left] # 不然此時左邊的數比基數大,則將該數放到右邊 data[left] = tmp # 最後將基準數量放回中間 return left # 返回基準數位置 if __name__=='__main__': data_list=[1,3,21,6,50,33,34,58,66] quick_sort(data_list,0,len(data_list)-1) print(data_list) ###結果:[1, 3, 6, 21, 33, 34, 50, 58, 66]
堆定義:本質是一個徹底二叉樹,若是根節點的值是全部節點的最小值稱爲小根堆,若是根節點的值是全部節點的最大值,稱爲大根堆。
效率:O(nlogn)
原理:
demo:
def sift(data, low, high): """ 調整堆函數 :param data: 帶排序的數據列表 :param low: 值較小的節點的位置,能夠理解爲是根節點 :param high:值較大的節點的位置 :return: """ i = low j = 2 * i # 父節點i所對應的左孩子 tmp = data[i] # 最較小節點的值 while j <= high: if j < high and data[j] < data[j + 1]: # 若是右孩子比左孩子大則把j指向右節點 j += 1 # 指向右節點 if tmp < data[j]: # 若是此時位置較小的節點值比該節點值小,則將該節點上浮最爲新的父節點,並調整該節點雙親 data[i] = data[j] i = j # 調整該節點的雙親的位置 j = 2 * i else: break # 不然表明本次調整已經完成,而且節點i已經無值 data[i] = tmp # 最後將被調整節點的值放到i節點上(空出的位置) def heap_sort(data): """ 堆排序 :param data: 待排序的數據列表 :return: """ n = len(data) for i in range(n // 2 - 1, -1, -1): sift(data, i, n - 1) # 構建堆 for i in range(n - 1, -1, -1): # 調整過程,從最後一個元素開始交換 data[0], data[i] = data[i], data[0] # 交換 sift(data, 0, i - 1) # 開始調整 if __name__ == '__main__': import random data_list = [1, 3, 21, 6, 50, 33, 34, 58, 66] random.shuffle(data_list) # 打亂列表數據 print("pre:", data_list) heap_sort(data_list) print("after:", data_list) #結果: #pre: [66, 3, 58, 34, 1, 33, 21, 6, 50] #after: [1, 3, 6, 21, 33, 34, 50, 58, 66]
效率:O(nlogn)
空間複雜度:O(n)
原理:
demo:
def merge(data, low, mid, high): """ 合併函數 :param data: 數據列表 :param low: 列表開頭位置 :param mid: 分割中間位置 :param high: 列表最後位置 :return: """ i = low # 第一個指針 j = mid + 1 # 第二個指針 tmp = [] # 臨時存放的列表 while i <= mid and j <= high: # 分割的列表當兩邊都有數才進行 if data[i] < data[j]: tmp.append(data[i]) i += 1 # 低的指針往右移動 else: tmp.append(data[j]) # 右邊大,存右邊的數 j += 1 # 同時指針右移動 while i <= mid: # 左邊分割有剩下 tmp.append(data[i]) i += 1 while j <= high: # 右邊有剩下 tmp.append(data[j]) j += 1 data[low:high + 1] = tmp # 最後將tmp中的數寫入到原來的列表中 def merge_sort(data, low, high): """ 歸併排序 :param data: 待排序的數據列表 :param low: 數據列表開始位置 :param high: 數據列表結束位置 :return: """ if low < high: # 至少有兩個元素才進行 mid = (low + high) // 2 # 分割 merge_sort(data, low, mid) # 遞歸分割上一部分 merge_sort(data, mid + 1, high) # 遞歸分割下一部分 merge(data, low, mid, high) # 合併 if __name__ == '__main__': import random data_list = [1, 3, 21, 6, 50, 33, 34, 58, 66] random.shuffle(data_list) # 打亂列表數據 print("pre:", data_list) merge_sort(data_list, 0, len(data_list) - 1) print("after:", data_list) #結果: #pre: [21, 3, 33, 58, 34, 66, 1, 6, 50] #after: [1, 3, 6, 21, 33, 34, 50, 58, 66]
效率:與增量有關,O(n1+£)其中<0£<1,如增量爲2k-1 複雜度爲O(n3/2)
原理:
def shell_sort(data): """ 希爾排序 :param data:待排序的數據列表 :return: """ d1 = len(data) // 2 # 設置分割大小爲d1, while d1 > 0: for i in range(d1, len(data)): tmp = data[i] # 當前分割元素位置 j = i - d1 # 上一個分割元素位置 while j >= 0 and tmp < data[j]: # 上一個元素分割位置比當前分割位置要大,則須要調整位置 data[j + d1] = data[j] # 後移動當前分割元素位置 j -= d1 # 往前移d1 data[j + d1] = tmp d1 //= 2 # 繼續分割 if __name__ == '__main__': import random data_list = [1, 3, 21, 6, 50, 33, 34, 58, 66] random.shuffle(data_list) # 打亂列表數據 print("pre:", data_list) shell_sort(data_list) print("after:", data_list) #結果: #pre: [3, 66, 58, 34, 33, 50, 6, 21, 1] #after: [1, 3, 6, 21, 33, 34, 50, 58, 66]