數據結構與算法
我們可以舉一個不太恰當的例子!
我們把敲代碼比作一場戰爭,我們程序猿(程序媛)可以看成是一個一個的將軍,而我們寫的代碼就可以看成是士兵或者兵器,那麼數據結構和算法相當於什麼呢?
答曰:兵法!
我們先開看一道題:
如果a+b+c=1000,且a2 + b2 = c2(a,b,c爲自然數),如何求出所有a、b、c可能的組合?
第一次嘗試
import time
start_time = time.time()
# 注意是三重循環
for a in range(0,1001):
for b in range(0,1001):
for c in range(0,1001):
if a**2 + b**2 == c**2 and a+b+c == 1000:
print("a,b,c:%d,%d,%d" %(a,b,c))
end_time = time.time()
print("elapsed:%f"%(end_time - start_time))
print("complete!")
運行時間我已經等的不耐煩了!
a,b,c:0,500,500
a,b,c:200,375,425
a,b,c:375,200,425
a,b,c:500,0,500
elapsed:325.548057
complete!
這個時間等的我夠夠的!每個計算機的處理能力可能不一樣,所以,有的可能比我這個還短。
算法的概念
算法是計算機處理信息的本質,因爲計算機程序本質上是一個算法來告訴計算機確切的步驟來執行一個指定的任務。
對於算法而言,實現的語言並不重要,重要的是思想。
算法的五大特性
- 輸入:算法具有0個或多個輸入
- 輸出:算法至少有1個或多個輸出
- 有窮性:算法在有限的步驟之後會自動結束而不會無限循環,並且每一個步驟可以在可接受的時間內完成
- 確定性:算法中的每一步都有確定的含義,不會出現二義性
- 可行性:算法的每一步都是可行的,也就是說每一步都能夠執行有限的次數完成
第二次嘗試
import time
start_time = time.time()
# 雙重循環
for a in range(0,1001):
for b in range(0,1001-a):
c = 1000 -a -b
if a ** 2 + b ** 2 == c ** 2:
print("a,b,c:%d,%d,%d" % (a, b, c))
end_time = time.time()
print("elapsed:%f"%(end_time - start_time))
print("complete!")
a,b,c:0,500,500
a,b,c:200,375,425
a,b,c:375,200,425
a,b,c:500,0,500
elapsed:0.452111
complete!
運行時間快的不知道幾倍!
單純依靠運行的時間比較算法的優劣並不一定是客觀準確的!
時間複雜度與“大O記法”
“大O記法”:對於單調的整數函數f,如存在一個整數函數g和實常數c>0,使得對於充分大的n總有f(n) <c*g(n),就說函數g是f的一個漸近函數(忽略常數),記爲f(n)=O(g(n))。也就是說,在趨向無窮的極限意義下,函數f的增長速度受到函數g的約束,也就是函數f和g的特徵相似。
時間複雜度:假設存在函數g,使得算法A處理規模爲n的問題示例所用時間爲T(n)=O(g(n)),則稱O(g(n))爲算法A的漸近時間複雜度,簡稱時間複雜度,記爲T(n)
如何理解“大O記法”
對於算法的時間性質和空間性質,最重要的是其數量級和趨勢,這些是分析算法效率的主要部分。例如,可以認爲3n2和100n2屬於同一個量級,如果兩個算法處理同樣規模實例的代價分別爲這兩個函數,就認爲他們的效率差不多,都爲n2級。
最壞時間複雜度
分析算法時,存在幾種可能的考慮:
- 算法完成工作最少需要多少基本操作,即爲最優時間複雜度
- 算法完成工作最多需要多少基本操作,即爲最壞時間複雜度
- 算法完成工作平均需要多少基本操作,即爲平均時間複雜度
最優時間複雜度,價值意義不大,反應出最樂觀的最理想的情況,沒有參考價值。
對於最壞時間複雜度,提供了一種保障,表明算法在此種誠度的基本操作中一定能完成工作。
對於平均時間複雜度,是對算法的一個全面評價,因此她完整全面的反映了這個算法的性質。
因此,我們主要關注算法的最壞情況,就是最壞時間複雜度。
時間複雜度的幾條基本計算規則
- 基本操作,只有常數項,認爲其時間複雜度爲O(1)
- 順序結構,時間複雜度按加法進行計算
- 循環結構,時間複雜度按乘法進行計算
- 分支結構,時間複雜度取最大值
- 判斷一個算法的效率時,往往只需要關注操作數量的最高次項,其他次要項和常數項可以忽略
- 在沒有特殊說明時,我們所分析的算法的時間複雜度都是指最壞時間複雜度
空間複雜度
類似於時間複雜度的討論,一個算法的空間複雜度S(n)定義爲該算法所耗費的存儲空間,他也是問題規模n的函數。
空間複雜度是對一個算法在運行過程中臨時佔用存儲空間大小的量度。
算法的時間複雜度和空間複雜度合稱算法的複雜度。
算法分析
- 第一次嘗試的算法核心部分
for a in range(0,1001):
for b in range(0,1001):
for c in range(0,1001):
if a**2 + b**2 == c**2 and a+b+c == 1000:
print("a,b,c:%d,%d,%d" %(a,b,c))
時間複雜度:
T(n) = O(n * n * n) = O(n3)
- 第一次嘗試的算法核心部分
for a in range(0,1001):
for b in range(0,1001-a):
c = 1000 -a -b
if a ** 2 + b ** 2 == c ** 2:
print("a,b,c:%d,%d,%d" % (a, b, c))
時間複雜度:
T(n) = O(n * n * (1+1)) = O(n * n) = O(n2)
由此可見,我們嘗試的第二種算法要比第一種算法的時間複雜度好的多。
常見時間複雜度
常見時間複雜度之間的關係
末尾,來做個小練習吧!
評論告訴我答案!
O(5)
O(2n+1)
O(n2+n+1)
O(3n3+1)