1. 程式人生 > >python資料結構和演算法【總結】

python資料結構和演算法【總結】

一。什麼是演算法

演算法是計算機處理資訊的本質,因為計算機程式本質上是一個演算法來告訴計算機確切的步驟來執行一個指定的任務。一般地,當演算法在處理資訊時,會從輸入裝置或資料的儲存地址讀取資料,把結果寫入輸出裝置或某個儲存地址供以後再呼叫。

通俗的來講,演算法就是你解決問題的思路。

二。演算法的五大特性

  1. 輸入: 演算法具有0個或多個輸入
  2. 輸出: 演算法至少有1個或多個輸出
  3. 有窮性: 演算法在有限的步驟之後會自動結束而不會無限迴圈,並且每一個步驟可以在可接受的時間內完成
  4. 確定性:演算法中的每一步都有確定的含義,不會出現二義性
  5. 可行性:演算法的每一步都是可行的,也就是說每一步都能夠執行有限的次數完成

三。時間複雜度

我們可以通過演算法的執行來判斷演算法的優劣。但這個執行時間本身又收到程式執行環境(硬體)的影響。有沒有什麼可以客觀的衡量演算法的優劣呢?  ----------時間複雜度

我們假定計算機執行演算法每一個基本操作的時間是固定的一個時間單位,那麼有多少個基本操作就代表會花費多少時間單位。

每臺機器執行的總時間不同,但同一演算法執行的步驟數量在每臺機器上是相同的。

時間複雜度可以用步驟的數量來表示,從而來表示演算法的優劣

在計算時間複雜度時注意:  N 問題的規模    走勢的影響    數量級

                 - 演算法的速度並非指時間,不是以秒為單位;而是運算元的增速。從增量的角度度量的。

                 - 平時說的演算法的速度,指的是隨著輸入的增加,其執行時間將會以什麼樣的速度進行增加。

---------------------------------------------------------------------------------------------------

“大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)

---------------------------------------------------------------------------------------------------------

  • 時間複雜度的計算

1.基本操作, 基本步驟,(一行語句)隨著n的增大,不受影響。即只有常數項,認為其時間複雜度為O(1)

2.順序結構,時間複雜度按假髮進行計算

3.迴圈結構,時間複雜度按乘法進行計算

4.分支結構,時間複雜度取最大值

5.判斷一個演算法的效率,往往只需要關注運算元量的最高次項 ,其他次要項和常數項可以忽略

6.在沒有特殊說明時,我們所分析的演算法的時間複雜度都是值最壞時間複雜度

常見時間複雜度

 

執行次數函式舉例

非正式術語
12 O(1) 常數階
2n+3 O(n) 線性階
3n2+2n+1 O(n2) 平方階
5log2n+20 O(logn) 對數階
2n+3nlog2n+19 O(nlogn) nlogn階
6n3+2n2+3n+4 O(n3) 立方階
2n O(2n) 指數階

 

 

四。資料結構

一個學生的資訊可以通過list儲存,也可以用字典dict的方式儲存。在這兩種的儲存放下下查詢學生的資訊時,時間複雜度時不相同的。

        我們為了解決問題,需要將資料儲存下來,然後根據資料的儲存方式來設計演算法實現進行處理,那麼資料的儲存方式不同就會導致需要不同的演算法進行處理。我們希望演算法解決問題的效率越快越好,於是我們就需要考慮資料究竟如何儲存的問題,這就是資料結構。

所以演算法和資料結構是不可分割的

       資料是一個抽象的概念,將其進行分類後得到程式設計語言中的基本型別。如:int  float   char 等。而計算機能正真儲存在物理單元中和使用的也只有基本的資料型別。資料元素之間不是獨立的,存在特定的關係,這些關係便是結構。比如,python中的list,他是由基本的資料型別組成,表達的是這些資料之間一定的關係。

      python給我們提供了很多現成的資料結構,這些系統自己定義好的,不需要我們自己去定義的資料結構叫做python的內建資料結構,比如列表,元組,字典。而有些資料組織方式,python系統裡面沒有直接定義,需要我們自己去定義實現這些資料的組織方式,這些資料組織方式稱之為python的擴充套件資料結構,比如棧,佇列等。

五。演算法與資料結構的區別

將基本的資料型別利用一定的關係組織到一起,就是我們所說的資料結構。

資料結構只是靜態的描述了資料元素之間的關係。

高效的程式需要在資料結構的基礎上設計和選擇演算法。

程式 = 資料結構 + 演算法

總結:演算法是為了解決實際問題而設計的,資料結構是演算法需要處理的問題載體

六。抽象資料型別(Abstract Data Type)

數學模型(資料結構)+數學模型上的一組操作,操作的具體實現無需定義

抽象資料型別(ADT)的含義是指一個數學模型以及定義在此數學模型上的一組操作。即把資料型別和資料型別上的運算捆在一起,進行封裝。引入抽象資料型別的目的是把資料型別的表示和資料型別上運算的實現與這些資料型別和運算在程式中的引用隔開,使它們相互獨立。

最常用的資料運算有五種:

  • 插入
  • 刪除
  • 修改
  • 查詢
  • 排序

 

七。記憶體

所說的型別,就是需要佔用幾個位元組的記憶體大小(型別不同佔用的空間也不同)  --------存

我取出的數個位元組以什麼樣的型別來對待。int 4個位元組     char  1個位元組

所有的高階資料結構都是有基本的資料結構構成

比如 int 佔用四個位元組   int 1    在記憶體中儲存的形式為 00000000    00000000  00000000 00000001

 

八。順序表

將資料存放在記憶體中的一段連續的空間上

 

圖a表示的是順序表的基本形式,資料元素本身連續儲存,每個元素所佔的儲存單元大小固定相同(資料型別相同),元素的下標是其邏輯地址,而元素儲存的實體地址(實際記憶體地址)可以通過儲存區的起始地址Loc (e0)加上邏輯地址(第i個元素)與儲存單元大小(c)的乘積計算而得,即:

Loc(ei) = Loc(e0) + c*i

故,訪問指定元素時無需從頭遍歷,通過計算便可獲得對應地址,其時間複雜度為O(1)。

如果元素的大小不統一,則須採用圖b的元素外接的形式,將實際資料元素另行儲存,而順序表中各單元位置儲存對應元素的地址資訊(即連結)。由於每個連結所需的儲存量相同,通過上述公式,可以計算出元素連結的儲存位置,而後順著連結找到實際儲存的資料元素。注意,圖b中的c不再是資料元素的大小,而是儲存一個連結地址所需的儲存量,這個量通常很小。

圖b這樣的順序表也被稱為對實際資料的索引,這是最簡單的索引結構。

0下標可以理解為偏移量,這樣在定址的時候特方便

 

順序表結構

一個順序表的完整資訊包括兩部分,一部分是表中的元素集合,另一部分是為實現正確操作而需記錄的資訊,即有關表的整體情況的資訊,這部分資訊主要包括元素儲存區的容量和當前表中已有的元素個數兩項。

順序表的兩種基本方式

圖a為一體式結構,儲存表資訊的單元與元素儲存區以連續的方式安排在一塊儲存區裡,兩部分資料的整體形成一個完整的順序表物件。

一體式結構整體性強,易於管理。但是由於資料元素儲存區域是表物件的一部分,順序表建立後,元素儲存區就固定了。

圖b為分離式結構,表物件裡只儲存與整個表有關的資訊(即容量和元素個數),實際資料元素存放在另一個獨立的元素儲存區裡,通過連結與基本表物件關聯。

元素儲存區替換

一體式結構由於順序表資訊區與資料區連續儲存在一起,所以若想更換資料區,則只能整體搬遷,即整個順序表物件(指儲存順序表的結構資訊的區域)改變了。

分離式結構若想更換資料區,只需將表資訊區中的資料區連結地址更新即可,而該順序表物件不變。

元素儲存區擴充

採用分離式結構的順序表,若將資料區更換為儲存空間更大的區域,則可以在不改變表物件的前提下對其資料儲存區進行了擴充,所有使用這個表的地方都不必修改。只要程式的執行環境(計算機系統)還有空閒儲存,這種表結構就不會因為滿了而導致操作無法進行。人們把採用這種技術實現的順序表稱為動態順序表,因為其容量可以在使用中動態變化。

擴充的兩種策略

  • 每次擴充增加固定數目的儲存位置,如每次擴充增加10個元素位置,這種策略可稱為線性增長。

    特點:節省空間,但是擴充操作頻繁,操作次數多。

  • 每次擴充容量加倍,如每次擴充增加一倍儲存空間。

    特點:減少了擴充操作的執行次數,但可能會浪費空間資源。以空間換時間,推薦的方式。

順序表的操作

插入           1)對未插入                 2)保序插入   O(n)                  3)不保序插入

刪除           2)對尾刪除                 2)保序刪除                      3)不保序刪除

 

 

Python中的順序表

Python中的list和tuple兩種型別採用了順序表的實現技術,具有前面討論的順序表的所有性質。

tuple是不可變型別,即不變的順序表,因此不支援改變其內部狀態的任何操作,而其他方面,則與list的性質類似。

list的基本實現技術

Python標準型別list就是一種元素個數可變的線性表,可以加入和刪除元素,並在各種操作中維持已有元素的順序(即保序),而且還具有以下行為特徵:

  • 基於下標(位置)的高效元素訪問和更新,時間複雜度應該是O(1);

    為滿足該特徵,應該採用順序表技術,表中元素儲存在一塊連續的儲存區中。

  • 允許任意加入元素,而且在不斷加入元素的過程中,表物件的標識(函式id得到的值)不變。

    為滿足該特徵,就必須能更換元素儲存區,並且為保證更換儲存區時list物件的標識id不變,只能採用分離式實現技術。

在Python的官方實現中,list就是一種採用分離式技術實現的動態順序表。這就是為什麼用list.append(x) (或 list.insert(len(list), x),即尾部插入)比在指定位置插入元素效率高的原因。

在Python的官方實現中,list實現採用瞭如下的策略:在建立空表(或者很小的表)時,系統分配一塊能容納8個元素的儲存區;在執行插入操作(insert或append)時,如果元素儲存區滿就換一塊4倍大的儲存區。但如果此時的表已經很大(目前的閥值為50000),則改變策略,採用加一倍的方法。引入這種改變策略的方式,是為了避免出現過多空閒的儲存位置。