數據結構和算法相當於兵法,如果我們常看兵法,便可做到胸有成竹,有時會事半功倍!同樣,如果我們常看數據結構與算法,我們寫程序時也能遊刃有餘、明察秋毫,遇到問題時亦能入木三分、迎刃而解.
數據結構和算法是一名程序開發人員的必備基本功,不是一朝一夕就能練成絕世高手的。冰凍三尺非一日之寒,需要我們平時不斷的主動去學習積累。
算法的五大特性:
- 輸入
- 輸出
- 有窮性:算法在有限步驟之後會自動結束,而不會無限循環,而且每一步都是在有限的時間內完成的。
- 確定性:算法中的每一步都有確定的含義,不會出現二義性。
- 可行性:算法的每一步驟都是可行的,也就是每一步都會執行有限的次數後完成。
算法的執行效率
我們都知道,一個算法好壞直觀上都是通過算法執行的快慢來做評判的,但是如果一個算法在兩臺不同的機器上執行,一個機器cpu較好,一個較差,此時執行時間明顯是有差異的,所以單靠執行時間來評判算法的好壞是不一定客觀準確地。
假定算法執行的每一個基本操作的時間單位是固定的,那麼一個算法有多少個基本操作就代表要花費多少個時間單位。所以我們可以用這種通過評判一個算法的基本操作的方法,來客觀反映一個算法的時間效率,也就是“大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)
- 順序結構,時間複雜度按加法進行計算
- 循環結構,時間複雜度按乘法進行計算
- 分支結構,時間複雜度取最大值
- 判斷一個算法的效率時,往往只需要關注操作數量的最高次項,其它次要項和常數項可以忽略
- 在沒有特殊說明時,我們所分析的算法的時間複雜度都是指最壞時間複雜度
比如下面這道題:
如果 a+b+c=1000,且 a2+b2=c2(a,b,c 爲自然數),如何求出所有a、b、c可能的組合?
第一次嘗試算法核心部分:
for a in range(1001):
for b in range(1001):
for c in range(1001):
if a*a+b*b==c*c and a+b+c=1000:
print("(a,b,c:%d,%d,%d)",%(a,b,c))
時間複雜度:
T(n) = O(nnn) = O(n3)
第二次嘗試:
for a in range(1001):
for b in range(1001):
c=1000-a-b
if a*a + b*b =c * c:
print("(a,b,c:%d,%d,%d)",%(a,b,c))
時間複雜度:
T(n) = O(nnn) = O(n2)
常見時間複雜度
所消耗的時間從小到大:
O(1)<O(log(n))<O( n) < O(nlog(n)) <O(n2)<O(n2log(n)) <O(n3)<O(2n)<O(n!)<O(nn)
算法與數據結構的區別
數據結構只是靜態的描述了數據元素之間的關係。
高效的程序需要在數據結構的基礎上設計和選擇算法。
程序 = 數據結構 + 算法
總結:算法是爲了解決實際問題而設計的,數據結構是算法需要處理的問題載體
抽象數據類型(Abstract Data Type)
抽象數據類型(ADT)的含義是指一個數學模型以及定義在此數學模型上的一組操作。即把數據類型和數據類型上的運算捆在一起,進行封裝。引入抽象數據類型的目的是把數據類型的表示和數據類型上運算的實現與這些數據類型和運算在程序中的引用隔開,使它們相互獨立。
最常用的數據運算有五種:
- 插入
- 刪除
- 修改
- 查找
- 排序