Python中的列表、元組到底是個啥?

前言

數據結構入門後第一個接觸到的應該就是順序表了,順序表應該還是比較好實現的,將元素順序地存放在一塊連續的存儲區裏,元素間的順序關係由它們的存儲順序自然表示。順序表的使用好處在於物理存儲上是一連串相鄰的地址,當你的第一個元素唯一確定存儲地址之後,後面的元素就可以順着找到且不需要什麼特別的操作。其實查閱官方文檔就可以發現,Python中的列表等高級數據類型就是用順序表來實現的,這就更加體現了數據結構的語言無關性。

順序表的基本形式

在這裏插入圖片描述
圖a是順序表的基本實現形式,可以看到其數據元素是連續存儲,每個數據佔用相同大小的存儲空間,元素的下標是其邏輯地址,而元素存儲的物理地址(實際內存地址)可以通過存儲區的起始地址Loc (e0)加上邏輯地址(第i個元素)與存儲單元大小(c)的乘積計算而得,即:
Loc(ei) = Loc(e0) + c*i
因此,順序表無需從頭開始一個一個的遍歷,只要知道首地址和一個元素佔用的存儲空間就可以計算得到其他元素的地址。
這種方式存儲有其好處也有不足,不足在於存儲不同數據類型的元素時,單一的相同的存儲空間可能會使某個數據存不進去也有可能造成空間的浪費。像Python中的列表是可以存儲不同數據類型的元素的,這就說明順序表有其他的存儲方式,就像是圖b所示的元素外置的順序表。
何爲元素外置?簡單來說就是要存儲的數據元素單獨存儲,順序表還是那個順序表,只不過在順序表中存儲的東西現在變成了原先要放在這裏的數據的地址,然後我們通過這個地址來找到這個數據元素。打個比方就是說哈,咱們一個班裏有好多個人,每個人的名字字數不同是吧,有的人兩個,有的人三個,還有四個五個的…我們要是直接用順序表存他們名字的話,字數不同,空間不好統一,但是他們每個人的學號的存儲空間是一樣的吧,我們就按照順序表存儲他們的學號,然後同個學號鏈接到每一個人的名字。

順序表的結構與實現

順序表的結構

在這裏插入圖片描述
一個順序表的完整信息包括兩部分,一部分是表中的元素集合,另一部分是爲實現正確操作而需記錄的信息,即有關表的整體情況的信息,這部分信息主要包括元素存儲區的容量和當前表中已有的元素個數兩項。

順序表的實現

順序表的兩種基本實現方式
在這裏插入圖片描述
圖a爲一體式結構,存儲表信息的單元與元素存儲區以連續的方式安排在一塊存儲區裏,兩部分數據的整體形成一個完整的順序表對象。一體式結構的好處在於整體性強,易於管理,但是由於數據元素存儲區域是表對象的一部分,順序表創建後,元素存儲區就固定了。後面想要添加元素的話就受限於存儲空間的大小。
圖b爲分離式結構,表對象裏只保存與整個表有關的信息(即容量和元素個數),實際數據元素存放在另一個獨立的元素存儲區裏,通過鏈接與基本表對象關聯。這種方式就解決了上面一體式存儲的問題,分離式結構對元素的修改就比較方便。
一體式結構由於順序表信息區與數據區連續存儲在一起,所以若想更換數據區,則只能整體搬遷,即整個順序表對象(指存儲順序表的結構信息的區域)改變了。
分離式結構若想更換數據區,只需將表信息區中的數據區鏈接地址更新即可,而該順序表對象不變。
採用分離式結構的順序表,若將數據區更換爲存儲空間更大的區域,則可以在不改變表對象的前提下對其數據存儲區進行了擴充,所有使用這個表的地方都不必修改。只要程序的運行環境(計算機系統)還有空閒存儲,這種表結構就不會因爲滿了而導致操作無法進行。人們把採用這種技術實現的順序表稱爲動態順序表,因爲其容量可以在使用中動態變化。
當然我們的元素擴充也是要講策略的:
每次擴充增加固定數目的存儲位置,如每次擴充增加10個元素位置,這種策略可稱爲線性增長。線性擴充可以節省空間,但是擴充操作頻繁,操作次數多。
每次擴充容量加倍,如每次擴充增加一倍存儲空間。倍數擴充減少了擴充操作的執行次數,但可能會浪費空間資源。以空間換時間,是推薦的方式。

順序表的操作

增加元素

在這裏插入圖片描述a. 尾端加入元素,時間複雜度爲O(1)
b. 非保序的加入元素(不常見),時間複雜度爲O(1)
c. 保序的元素加入,時間複雜度爲O(n)

刪除元素

在這裏插入圖片描述
a. 刪除表尾元素,時間複雜度爲O(1)
b. 非保序的元素刪除(不常見),時間複雜度爲O(1)
c. 保序的元素刪除,時間複雜度爲O(n)

具體可見我的前一篇博文列表的操作。

Python中的順序表

前面就說了列表是Python中的一種順序表,元組也是,不過元組是不可變順序表,不能執行改變元素的操作,其他的性質跟列表是一樣的。

在Python的官方實現中,list就是一種採用分離式技術實現的動態順序表。這就是爲什麼用list.append(x) (或 list.insert(len(list), x),即尾部插入)比在指定位置插入元素效率高的原因。
在Python的官方實現中,list實現採用瞭如下的策略:在建立空表(或者很小的表)時,系統分配一塊能容納8個元素的存儲區;在執行插入操作(insert或append)時,如果元素存儲區滿就換一塊4倍大的存儲區。但如果此時的表已經很大(目前的閥值爲50000),則改變策略,採用加一倍的方法。引入這種改變策略的方式,是爲了避免出現過多空閒的存儲位置。

後記

圖片和某些代碼來源於我學習的資料
接下來就要接觸到鏈表了啊,一想到C語言中的指針就腦殼痛,不知道Python是如何解決的呢?歡迎關注我的下一篇博客!
湖北新增確診爲0了!今天才知道,開學的日子應該就不遠了叭,武漢加油!!!
新手上路,技術有限,不喜勿噴!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章