一、什麼是算法
python
算法(algorithm):就是定義良好的計算過程,他取一個或一組的值爲輸入,併產生出一個或一組值做爲輸出。簡單來講算法就是一系列的計算步驟,用來將輸入數據轉化成輸出結果。算法
mark:咱們能夠把全部的算法想象爲一本「菜譜」,特定的算法好比菜譜中的的一道「老醋花生米」的製做流程,只要按照菜譜的要求製做老醋花生米,那麼誰均可以作出一道好吃的老醋花生米。so,這個作菜的步驟就能夠理解爲:「解決問題的步驟」
數組
二、算法的意義app
假設計算機無限快,而且計算機存儲容器是免費的,咱們還須要各類亂七八糟的算法嗎?若是計算機無限快,那麼對於某一個問題來講,任何一個均可以解決他的正確方法均可以的!dom
固然,計算機能夠作到很快,可是不能作到無限快,存儲也能夠很便宜可是不能作到免費。函數
那麼問題就來了效率:解決同一個問題的各類不一樣算法的效率經常相差很是大,這種效率上的差距的影響每每比硬件和軟件方面的差距還要大。性能
三、如何選擇算法測試
第一首先要保證算法的正確性優化
一個算法對其每個輸入的實例,都能輸出正確的結果並中止,則稱它是正確的,咱們說一個正確的算法解決了給定的計算問題。不正確的算法對於某些輸入來講,可能根本不會中止,或者中止時給出的不是預期的結果。然而,與人們對不正確算法的見解想反,若是這些算法的錯誤率能夠獲得控制的話,它們有時候也是有用的。可是通常而言,咱們仍是僅關注正確的算法!ui
第二分析算法的時間複雜度
算法的時間複雜度反映了程序執行時間隨輸入規模增加而增加的量級,在很大程度上能很好反映出算法的好壞。
一、什麼是時間複雜度
一個算法花費的時間與算法中語句的執行次數成正比例,哪一個算法中語句執行次數多,它花費時間就多。一個算法中的語句執行次數稱爲語句頻度或時間頻度。記爲T(n)。
通常狀況下,算法中基本操做重複執行的次數是問題規模n的某個函數,用T(n)表示,如有某個輔助函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值爲不等於零的常數,則稱f(n)是T(n)的同數量級函數。記做T(n)=O(f(n)),稱O(f(n)) 爲算法的漸進時間複雜度,簡稱時間複雜度。
二、時間複雜度的計算方法
一個算法執行所耗費的時間,從理論上是不能算出來的,必須上機運行測試才能知道。但咱們不可能也沒有必要對每一個算法都上機測試由於該方法有兩個缺陷:
因此只需知道哪一個算法花費的時間多,哪一個算法花費的時間少就能夠了。而且一個算法花費的時間與算法中語句的執行次數成正比例,哪一個算法中語句執行次數多,它花費時間就多。
通常狀況下,算法的基本操做重複執行的次數是模塊n的某一個函數f(n),所以,算法的時間複雜度記作:T(n)=O(f(n))。隨着模塊n的增大,算法執行的時間的增加率和f(n)的增加率成正比,因此f(n)越小,算法的時間複雜度越低,算法的效率越高。
在計算時間複雜度的時候,先找出算法的基本操做,而後根據相應的各語句肯定它的執行次數,再找出T(n)的同數量級(它的同數量級有如下:1,Log2n ,n ,nLog2n ,n的平方,n的三次方,2的n次方,n!),找出後,f(n)=該數量級,若T(n)/f(n)求極限可獲得一常數c,則時間複雜度T(n)=O(f(n))。
三、常見的時間複雜度
常見的算法時間複雜度由小到大依次爲:
Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)
求解算法的時間複雜度的具體步驟:
若是算法中包含鑲套的循環,則基本語句一般是最內層的循環體,若是算法中包並列的循環,則將並列的循環時間複雜度相加,例如:
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luotianshuai' n = 100 for i in range(n): print(i) for i in range(n): ##每循i裏的一個元素,for循環內部嵌套的for循環就整個循環一次 for q in range(n): print(q)
第一個for循環的時間複雜度爲Ο(n),第二個for循環的時間複雜度爲Ο(n2),則整個算法的時間複雜度爲Ο(n+n2)=Ο(n2)。
Ο(1)表示基本語句的執行次數是一個常數,通常來講,只要算法中不存在循環語句,其時間複雜度就是Ο(1)。
其中Ο(log2n)、Ο(n)、 Ο(nlog2n)、Ο(n2)和Ο(n3)稱爲多項式時間,而Ο(2n)和Ο(n!)稱爲指數時間,計算機科學家廣泛認爲前者(即多項式時間複雜度的算法)是有效算法,把這類問題稱爲P(Polynomial,多項式)類問題,而把後者(即指數時間複雜度的算法)稱爲NP(Non-Deterministic Polynomial, 非肯定多項式)問題。在選擇算法的時候,優先選擇前者!
OK我懂對於沒有算法基礎的同窗,看起算法來也很頭疼,可是這個是基礎和重點,不會算法的開發不是一個合格的開發而且包括語言記得基礎也是須要好好整理的!加油吧~~ 我們在一塊兒看下時間複雜度的詳細說明吧
一、O(1)
#O(1) n = 100 sum = (1+n) * n/2 #執行一次 sum_1 = (n/2) - 10 #執行一次 sum_2 = n*4 - 10 + 8 /2 #執行一次
這個算法的運行次數函數是f(n)=3。根據咱們推導大O階的方法,第一步就是把常數項3改成1。在保留最高階項時發現,它根本沒有最高階項,因此這個算法的時間複雜度爲O(1)。
而且:若是算法的執行時間不隨着問題規模n的增加而增長,及時算法中有上千條語句,其執行的時間也不過是一個較大的常數。此類算法的時間複雜度記做O(1)
二、O(n2)
n = 100 for i in range(n): #執行了n次 for q in range(n): #執行了n2 print(q) #執行了n2
解:T(n)=2n2+n+1 =O(n2)
通常狀況下,對進循環語句只需考慮循環體中語句的執行次數,忽略該語句中步長加一、終值判別、控制轉移等成分,當有若干個循環語句時,算法的時間複雜度是由嵌套層數最多的循環語句中最內層語句的頻度f(n)決定的。
三、O(n)
#O(n) n =100 a = 0 #執行一次 b = 1#執行一次 for i in range(n): #執行n次 s = a +b #執行n-1次 b =a #執行n-1次 a =s #執行n-1次
解:T(n)=2+n+3(n-1)=4n-1=O(n)
四、Ο(n3)
#O(n3) n = 100 for i in range(n):#執行了n次 for q in range(n):#執行了n^2 for e in range(n):#執行了n^3 print(e)#執行了n^3
簡單點來去最大值是:Ο(n3)
五、經常使用的算法的時間複雜度和空間複雜度
排序法 | 平均時間 | 最差狀況 | 穩定度 | 額外空間 | 備註 |
冒泡排序 | Ο(n2) | Ο(n2) | 穩定 | O(1) | n小時較好 |
交換排序 | Ο(n2) | Ο(n2) | 不穩定 | O(1) | n小時較好 |
選擇排序 | Ο(n2) | Ο(n2) | 不穩定 | O(1) | n小時較好 |
插入排序 | Ο(n2) | Ο(n2) | 穩定 | O(1) | 大部分已排序時較好 |
快速排序 | Ο(nlogn) | Ο(n2) | 不穩定 | Ο(nlogn) | n較大時較好 |
希爾排序(SHELL) | Ο(log2n) | Ο(ns) 1<s<2 |
不穩定 | O(1) | s是所選分組 |
歸併排序 | Ο(log2n) | Ο(log2n) | 穩定 | O(1) | n大時較好 |
堆排序 | Ο(log2n) | Ο(log2n) | 不穩定 | O(1) | n大時較好 |
基數排序 | Ο(logRB) | Ο(logRB) | 穩定 | O(N) | B是真數(0-9) R是基數(個十百) |
排序算法是在更復雜的算法中的是一個構建基礎,因此先看下經常使用的排序。
一、冒泡排序
需求:
請按照從小到大對列表,進行排序==》:[69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619]
思路:相鄰兩個值進行比較,將較大的值放在右側,依次比較!
原理圖:
原理分析:
列表中有5個元素兩兩進行比較,若是左邊的值比右邊的值大,就用中間值進行循環替換!
既然這樣,咱們還能夠用一個循環把上面的循環進行在次循環,用表達式構造出內部循環!
代碼實現:
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luotianshuai' import random maopao_list = [13, 22, 6, 99, 11] ''' 原理分析: 列表中有5個元素兩兩進行比較,若是左邊的值比右邊的值大,就用中間值進行循環替換! 既然這樣,咱們還能夠用一個循環把上面的循環進行在次循環,用表達式構造出內部循環! ''' def handler(array): for i in range(len(array)): for j in range(len(array)-1-i): ''' 這裏爲何要減1,咱們看下若是裏面有5個元素咱們須要循環幾回?最後一個值和誰對比呢?對吧!因此須要減1 這裏爲何減i?,這個i是循環的下標,若是咱們循環了一次以後最後一隻值已是最大的了還有必要再進行一次對比嗎?沒有必要~ ''' print('left:%d' % array[j],'right:%d' % array[j+1]) if array[j] > array[j+1]: tmp = array[j] array[j] = array[j+1] array[j+1] = tmp if __name__ == '__main__': handler(maopao_list) print(maopao_list)
時間複雜度說明看下他的代碼複雜度會隨着N的增大而成指數型增加,而且根據判斷他時間複雜度爲Ο(n2)
二、選擇排序
需求:
請按照從小到大對列表,進行排序==》:[69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619]
思路:
第一次,從列表最左邊開始元素爲array[0],往右循環,從右邊元素中找到小於array[0]的元素進行交換,直到右邊循環完以後。
第二次,左邊第一個元素如今是最小的了,就從array[1],和剩下的array[1:-1]內進行對比,依次進行對比!
對比:
他和冒泡排序的區別就是,冒泡排序是相鄰的兩兩作對比,可是選擇排序是左側的「對比元素」和右側的列表內值作對比!
原理圖:
代碼實現:
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luotianshuai' xuanze_list = [13, 22, 6, 99, 11] print(range(len(xuanze_list))) def handler(array): for i in range(len(array)): ''' 循環整個列表 ''' for j in range(i,len(array)): ''' 這裏的小循環裏,循環也是整個列表可是他的起始值是i,當這一個小循環完了以後最前面的確定是已經排序好的 第二次的時候這個值是循環的第幾回的值好比第二次是1,那麼循環的起始值就是array[1] ''' if array[i] > array[j]: temp = array[i] array[i] = array[j] array[j] = temp # print(array) if __name__ == '__main__': handler(xuanze_list) print(xuanze_list)
選擇排序代碼優化:
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luotianshuai' import random import time def handler(array): for i in range(len(array)): smallest_index = i #假設默認第一個值最小 for j in range(i,len(array)): if array[smallest_index] > array[j]: smallest_index = j #若是找到更小的,記錄更小元素的下標 ''' 小的循環結束後在交換,這樣整個小循環就以前的選擇排序來講,少了不少的替換過程,就只替換了一次!提高了速度 ''' tmp = array[i] array[i] = array[smallest_index] array[smallest_index] = tmp if __name__ == '__main__': array = [] old_time = time.time() for i in range(50000): array.append(random.randrange(1000000)) handler(array) print(array) print('Cost time is :',time.time() - old_time)
三、插入排序
需求:
請按照從小到大對列表,進行排序==》:[69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619]
思路:
一個列表默認分爲左側爲排序好的,咱們拿第一個元素舉例,他左邊的全是排序好的,他右側是沒有排序好的,若是右側的元素小於左側排序好的列表的元素就把他插入到合適的位置
原理圖:
代碼實現:
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luotianshuai' import random import time chaoru_list = [69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619] def handler(array): for i in range(1,len(array)): position = i #剛開始往左邊走的第一個位置 current_val = array[i] #先把當前值存下來 while position > 0 and current_val < array[position -1]: ''' 這裏爲何用while循環,我們在判斷左邊的值得時候知道他有多少個值嗎?不知道,因此用while循環 何時停下來呢?當左邊沒有值得時候,或者當他大於左邊的值得時候! ''' array[position] = array[position - 1] #若是whille條件成立把當前的值替換爲他上一個值 ''' 好比一個列表: [3,2,4,1] 如今循環到 1了,他前面的元素已經循環完了 [2,3,4] 1 首先咱們記錄下當前這個position的值 = 1 [2,3,4,4] 這樣,就出一個位置了 在對比前面的3,1比3小 [2,3,3,4] 在替換一下他們的值 在對比2 [2,2,3,4] 最後while不執行了在進行替換'array[position] = current_val #把值替換' ''' position -= 1 #當上面的條件都不成立的時候{左邊沒有值/左邊的值不比本身的值小} array[position] = current_val #把值替換 if __name__ == '__main__': handler(chaoru_list) print(chaoru_list) ''' array = []#[69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619] old_time = time.time() for i in range(50000): array.append(random.randrange(1000000)) handler(array) print(array) print('Cost time is :',time.time() - old_time) '''
四、快速排序
設要排序的數組是A[0]……A[N-1],首先任意選取一個數據(一般選用數組的第一個數)做爲關鍵數據,而後將全部比它小的數都放到它前面,全部比它大的數都放到它後面,這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變更.他的時間複雜度是:O(nlogn) ~Ο(n2)
排序示例:
假設用戶輸入了以下數組:
建立變量i=0(指向第一個數據)[i所在位置紅色小旗子], j=5(指向最後一個數據)[j所在位置藍色小旗子], k=6(賦值爲第一個數據的值)。
i=0 j=3 k=6
i=2 j=3 k=6
若是i和j沒有碰頭的話,就遞加i找大的,尚未,就再遞減j找小的,如此反覆,不斷循環。注意判斷和尋找是同時進行的。
代碼實現:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author:luotianshuai import random import time def quick_sort(array,start,end): if start >= end: return k = array[start] left_flag = start right_flag = end while left_flag < right_flag: ''' left_flag = start 默認爲0 right_flag = end 默認爲傳來的列表總長度 當left_flag 小與right_flag的時候成立,說明左右兩邊的小旗子尚未碰頭(爲相同的值) ''' #右邊旗子 while left_flag < right_flag and array[right_flag] > k:#表明要繼續往左一移動小旗子 right_flag -= 1 ''' 若是上面的循環中止說明找到右邊比左邊的值小的數了,須要進行替換 ''' tmp = array[left_flag] array[left_flag] = array[right_flag] array[right_flag] = tmp #左邊旗子 while left_flag < right_flag and array[left_flag] <= k: #若是沒有找到比當前的值大的,left_flag 就+=1 left_flag += 1 ''' 若是上面的循環中止說明找到當前段左邊比右邊大的值,進行替換 ''' tmp = array[left_flag] array[left_flag] = array[right_flag] array[right_flag] = tmp #進行遞歸把問題分半 quick_sort(array,start,left_flag-1) quick_sort(array,left_flag+1,end) if __name__ == '__main__': array = [] # [69, 471, 106, 66, 149, 983, 160, 57, 792, 489, 764, 589, 909, 535, 972, 188, 866, 56, 243, 619] start_time = time.time() for i in range(50000): array.append(random.randrange(1000000)) quick_sort(array,0,len(array)-1) end_time = time.time() print(array) print(start_time,end_time) cost_time = end_time - start_time print('Cost time is :%d' % cost_time)