學習筆記,僅供參考
文章目錄
算法與數據結構–基於python
數據結構和算法簡介
- 什麼是數據結構
數據結構就是一些有關係的數據的集合,有順序表,鏈表,棧,隊列,樹,圖等結構,
我們的程序就等於數據結構+算法。
- 什麼是算法
算法(Algorithm)是指解題方案的準確而完整的描述,是一系列解決問題的清晰指令,算法代表着用系統的方法描述解決問題的策略機制;不同的算法可能用不同的時間、空間或效率來完成同樣的任務。一個算法的優劣可以用空間複雜度與時間複雜度來衡量;算法就是一種思路.
-
數據結構和算法的用處
-
寫出的程序可以更高效;
-
面對一些複雜問題可能無從下手,數據結構和算法可以鍛鍊邏輯思維。
-
算法引入
例題A
如果 a+b+c=1000,且 a²+b²=c²(a,b,c爲自然數),如何求出所有a、b、c可能的組合?(不使用數學公式)
枚舉法:
import time
start = time.time()
for a in range(1001):
# a取完讓b去取
for b in range(1001):
for c in range(1001):
if a + b + c == 1000 and a**2 + b**2 == c**2:
print(a,b,c)
end = time.time()
print('finish')
print('程序用時:',(end-start))
運行結果:
0 500 500
200 375 425
375 200 425
500 0 500
finish
程序用時: 829.0995240211487
算法的概念
算法是計算機處理信息的本質,因爲計算機程序本質上是用一個算法來告訴計算機確切的步驟,進而執行一個指定的任務。當算法在處理信息時,會從輸入設備或數據的存儲地址讀取數據,把結果寫入輸出設備或某個存儲地址供以後再調用。
算法是獨立存在的一種解決問題的方法和思想,對於算法而言,實現的語言並不重要,重要的是思想,算法有不同的語言實現版本(如C、Java、Python等)
-
算法的五大特性
-
輸入: 算法具有0個或多個輸入;
-
輸出: 算法至少有1個或多個輸出;
-
有窮性: 算法在有限的步驟之後會自動結束而不會無限循環,並且每一個步驟可以在可接受的時間內完成;
-
確定性:算法中的每一步都有確定的含義;
-
可行性:算法的每一步都是可行的.
-
例題A的優化
import time
start = time.time()
for a in range(1001):
# a取完讓b去取
for b in range(1001 - a):
# a,b已經確定了
c = 1000 - a - b
if a**2 + b**2 == c**2:
print(a,b,c)
end = time.time()
print('finish')
print('程序用時:',(end-start))
運行結果:
0 500 500
200 375 425
375 200 425
500 0 500
finish
程序用時: 2.3102197647094727
最直觀的評判算法優劣的標準,就是運行時間,可以看到改進的算法運行時長明顯小於枚舉法,所以改進的算法一定程度上優於枚舉法。
算法效率的衡量
- 執行時間反應算法效率
實現算法程序的執行時間可以反應出算法的效率,即算法的優劣
- 單純依據時間衡量可信麼?
單純依靠運行的時間來比較算法的優劣並不一定是客觀準確的;
程序的運行離不開計算機環境(包括硬件和操作系統),這些客觀原因會影響程序運行的速度並反應在程序的執行時間上。
時間複雜度與大O記法
假定計算機執行算法每一個基本操作的時間是固定的一個時間單位,那麼有多少個基本操作就代表會花費多少時間單位。
雖然對於不同的機器環境而言,確切的單位時間是不同的,但是對於算法進行多少個基本操作(即花費多少時間單位)在規模數量級上卻是相同的,由此可以忽略機器環境的影響,而客觀的反應算法的時間效率。
對於算法的時間效率,我們可以用大O記法來表示。
大O記法:對於單調的整數函數,如果存在一個整數函數和實常數,使得對於充分大的n總有,就說函數是的一個漸近函數(忽略常數),記爲。也就是說,在趨向無窮的極限意義下,函數的增長速度受到函數的約束,亦即函數與函數的特徵相似。
時間複雜度:假設存在函數,使得算法A處理規模爲n的問題示例所用時間爲,則稱爲算法A的漸近時間複雜度,簡稱時間複雜度,記爲
例題A的時間複雜度
我們用表示時間複雜度,則對於枚舉法來說,其時間複雜度爲,若用代表數據規模,則,若存在函數,使,因爲和不影響大局,即相比於的形式來說,對時間的影響微不足道,所以我們拋棄和,則算法的趨勢爲
如何理解大O記法
對於算法進行特別具體的細緻分析雖然很好,但在實踐中的實際價值有限。對於算法的時間性質和空間性質,最重要的是其數量級和趨勢,這些是分析算法效率的主要部分.
而計量算法基本操作數量的規模函數中那些常量因子可以忽略不計。例如,可以認爲和屬於同一個量級,如果兩個算法處理同樣規模實例的代價分別爲這兩個函數,就認爲它們的效率差不多,都爲級.
最壞時間複雜度
分析算法時,存在幾種需要考慮的情況:
-
算法完成工作最少需要多少基本操作,即最優時間複雜度。
-
算法完成工作最多需要多少基本操作,即最壞時間複雜度。
-
算法完成工作平均需要多少基本操作,即平均時間複雜度。
我們主要關注算法的最壞情況,亦即最壞時間複雜度。
時間複雜度的幾條基本計算規則
- 基本操作,即只有常數項,認爲就是
- 順序結構,時間複雜度按加法進行計算
- 循環結構(for),時間複雜度按乘法進行計算
- 分支結構(if),時間複雜度爲分支中的時間複雜度的最大值
剛纔的例題A中就存在分支結構(if)和循環結構(for):
for a in range(1001):
# a取完讓b去取
for b in range(1001):
for c in range(1001):
if a + b + c == 1000 and a**2 + b**2 == c**2:
print(a,b,c)
時間複雜度:
當我們的程序遇到if時,可能會執行if語句體裏的內容,也可能不執行,所以分支結構if中最多有1次操作,最少爲0次,而我們計算時間複雜度時,則用最大操作次數1來計算。
常見的時間複雜度
執行次數函數舉例 | 階 | 非正式術語 |
---|---|---|
12 | 常數階 | |
2n+3 | 線性階 | |
3n²+2n+1 | 平方階 | |
5log2n+20 | 對數階 | |
2n+3nlog2n+19 | 階 | |
6n³+2n²+3n+4 | 立方階 | |
2^n | 指數階 |
- 常見時間複雜度之間的關係
所消耗的時間從小到大:
O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(2^n) < O(n!) < O(n^n)