咱們如何用Python中的類型來保存一個公司的客戶信息? 若是想要快速的經過客戶姓名獲取其信息呢? python
實際上當咱們在思考這個問題的時候,咱們已經用到了數據結構。列表和字典均可以存儲一個班的客戶信息,可是想要在列表中獲取一名user的信息時,就要遍歷這個列表,其時間複雜度爲O(n),而使用字典存儲時,可將客戶姓名做爲字典的鍵,客戶信息做爲值,進而查詢時不須要遍歷即可快速獲取到客戶信息,其時間複雜度爲O(1)。算法
咱們爲了解決問題,須要將數據保存下來,而後根據數據的存儲方式來設計算法實現進行處理,那麼數據的存儲方式不一樣就會致使須要不一樣的算法進行處理。咱們但願算法解決問題的效率越快越好,因而咱們就須要考慮數據究竟如何保存的問題,這就是數據結構。數據結構
在上面的問題中咱們能夠選擇Python中的列表或字典來存儲客戶信息。列表和字典就是Python內建幫咱們封裝好的兩種數據結構。app
數據是一個抽象的概念,將其進行分類後獲得程序設計語言中的基本類型。如:int,float,char等。數據元素之間不是獨立的,存在特定的關係,這些關係即是結構。數據結構指數據對象中數據元素之間的關係。函數
Python給咱們提供了不少現成的數據結構類型,這些系統本身定義好的,不須要咱們本身去定義的數據結構叫作Python的內置數據結構,好比列表、元組、字典。而有些數據組織方式,Python系統裏面沒有直接定義,須要咱們本身去定義實現這些數據的組織方式,這些數據組織方式稱之爲Python的擴展數據結構,好比棧,隊列等。spa
數據結構只是靜態的描述了數據元素之間的關係。設計
高效的程序須要在數據結構的基礎上設計和選擇算法。code
程序 = 數據結構 + 算法對象
總結:算法是爲了解決實際問題而設計的,數據結構是算法須要處理的問題載體blog
抽象數據類型(ADT)的含義是指一個數學模型以及定義在此數學模型上的一組操做。即把數據類型和數據類型上的運算捆在一塊兒,進行封裝。引入抽象數據類型的目的是把數據類型的表示和數據類型上運算的實現與這些數據類型和運算在程序中的引用隔開,使它們相互獨立。
最經常使用的數據運算有五種:
在程序中,常常須要將一組(一般是同爲某個類型的)數據元素做爲總體管理和使用,須要建立這種元素組,用變量記錄它們,傳進傳出函數等。一組數據中包含的元素個數可能發生變化(能夠增長或刪除元素)。
對於這種需求,最簡單的解決方案即是將這樣一組元素當作一個序列,用元素在序列裏的位置和順序,表示實際應用中的某種有意義的信息,或者表示數據之間的某種關係。
這樣的一組序列元素的組織形式,咱們能夠將其抽象爲線性表。一個線性表是某類元素的一個集合,還記錄着元素之間的一種順序關係。線性表是最基本的數據結構之一,在實際程序中應用很是普遍,它還常常被用做更復雜的數據結構的實現基礎。
根據線性表的實際存儲方式,分爲兩種實現模型:
圖a表示的是順序表的基本形式,數據元素自己連續存儲,每一個元素所佔的存儲單元大小固定相同,元素的下標是其邏輯地址,而元素存儲的物理地址(實際內存地址)能夠經過存儲區的起始地址Loc (e0)加上邏輯地址(第i個元素)與存儲單元大小(c)的乘積計算而得,即:
Loc(ei) = Loc(e0) + c*i
故,訪問指定元素時無需從頭遍歷,經過計算即可得到對應地址,其時間複雜度爲O(1)。
若是元素的大小不統一,則須採用圖b的元素外置的形式,將實際數據元素另行存儲,而順序表中各單元位置保存對應元素的地址信息(即連接)。因爲每一個連接所需的存儲量相同,經過上述公式,能夠計算出元素連接的存儲位置,然後順着連接找到實際存儲的數據元素。注意,圖b中的c再也不是數據元素的大小,而是存儲一個連接地址所需的存儲量,這個量一般很小。
圖b這樣的順序表也被稱爲對實際數據的索引,這是最簡單的索引結構。
一個順序表的完整信息包括兩部分,一部分是表中的元素集合,另外一部分是爲實現正確操做而需記錄的信息,即有關表的總體狀況的信息,這部分信息主要包括元素存儲區的容量和當前表中已有的元素個數兩項。
圖a爲一體式結構,存儲表信息的單元與元素存儲區以連續的方式安排在一塊存儲區裏,兩部分數據的總體造成一個完整的順序表對象。
一體式結構總體性強,易於管理。可是因爲數據元素存儲區域是表對象的一部分,順序表建立後,元素存儲區就固定了。
圖b爲分離式結構,表對象裏只保存與整個表有關的信息(即容量和元素個數),實際數據元素存放在另外一個獨立的元素存儲區裏,經過連接與基本表對象關聯。
一體式結構因爲順序表信息區與數據區連續存儲在一塊兒,因此若想更換數據區,則只能總體搬遷,即整個順序表對象(指存儲順序表的結構信息的區域)改變了。
分離式結構若想更換數據區,只需將表信息區中的數據區連接地址更新便可,而該順序表對象不變。
採用分離式結構的順序表,若將數據區更換爲存儲空間更大的區域,則能夠在不改變表對象的前提下對其數據存儲區進行了擴充,全部使用這個表的地方都沒必要修改。只要程序的運行環境(計算機系統)還有空閒存儲,這種表結構就不會由於滿了而致使操做沒法進行。人們把採用這種技術實現的順序表稱爲動態順序表,由於其容量能夠在使用中動態變化。
擴充的兩種策略
每次擴充增長固定數目的存儲位置,如每次擴充增長10個元素位置,這種策略可稱爲線性增加。
特色:節省空間,可是擴充操做頻繁,操做次數多。
每次擴充容量加倍,如每次擴充增長一倍存儲空間。
特色:減小了擴充操做的執行次數,但可能會浪費空間資源。以空間換時間,推薦的方式。
如圖所示,爲順序表增長新元素111的三種方式
a. 尾端加入元素,時間複雜度爲O(1)
b. 非保序的加入元素(不常見),時間複雜度爲O(1)
c. 保序的元素加入,時間複雜度爲O(n)
a. 刪除表尾元素,時間複雜度爲O(1)
b. 非保序的元素刪除(不常見),時間複雜度爲O(1)
c. 保序的元素刪除,時間複雜度爲O(n)
Python中的list和tuple兩種類型採用了順序表的實現技術,具備前面討論的順序表的全部性質。
tuple是不可變類型,即不變的順序表,所以不支持改變其內部狀態的任何操做,而其餘方面,則與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),則改變策略,採用加一倍的方法。引入這種改變策略的方式,是爲了不出現過多空閒的存儲位置。