数据结构(python)
列表
list 在頭部進行插入是個相當耗時的操作(需要把后邊的元素一個一個挪個位置)。假如你需要頻繁在數組兩頭增刪,list 就不太合適。
數組是最常用到的一種線性結構,其實 python 內置了一個 array 模塊,但是大部人甚至從來沒用過它。 Python 的 array 是內存連續、存儲的都是同一數據類型的結構,而且只能存數值和字符。
最常用的還是 list 來實現一個固定長度、并且支持所有 Python 數據類型的數組 Array.
隊列
隊列(queue)是只允許在一端進行插入操作,而在另一端進行刪除操作的線性表。
隊列是一種先進先出的(First In First Out)的線性表,簡稱FIFO。允許插入的一端為隊尾,允許刪除的一端為隊頭。隊列不允許在中間部位進行操作!假設隊列是q=(a1,a2,……,an),那么a1就是隊頭元素,而an是隊尾元素。這樣我們就可以刪除時,總是從a1開始,而插入時,總是在隊列最后。這也比較符合我們通常生活中的習慣,排在第一個的優先出列,最后來的當然排在隊伍最后。
隊列的實現:
同棧一樣,隊列也可以用順序表或者鏈表實現。
操作
Queue() 創建一個空的隊列
enqueue(item) 往隊列中添加一個item元素
dequeue() 從隊列頭部刪除一個元素
is_empty() 判斷一個隊列是否為空
size() 返回隊列的大小
?
雙端隊列
雙端隊列(deque,全名double-ended queue),是一種具有隊列和棧的性質的數據結構。
雙端隊列中的元素可以從兩端彈出,其限定插入和刪除操作在表的兩端進行。雙端隊列可以在隊列任意一端入隊和出隊。
操作
Deque() 創建一個空的雙端隊列
add_front(item) 從隊頭加入一個item元素
add_rear(item) 從隊尾加入一個item元素
remove_front() 從隊頭刪除一個item元素
remove_rear() 從隊尾刪除一個item元素
is_empty() 判斷雙端隊列是否為空
size() 返回隊列的大小
?
棧
棧(stack),有些地方稱為堆棧,是一種容器,可存入數據元素、訪問元素、刪除元素,它的特點在于只能允許在容器的一端(稱為棧頂端指標,英語:top)進行加入數據(英語:push)和輸出數據(英語:pop)的運算。沒有了位置概念,保證任何時候可以訪問、刪除的元素都是此前最后存入的那個元素,確定了一種默認的訪問順序。
由于棧數據結構只允許在一端進行操作,因而按照后進先出(LIFO, Last In First Out)的原理運作。
棧結構實現:
棧可以用順序表實現,也可以用鏈表實現。
棧的操作
Stack() 創建一個新的空棧
push(item) 添加一個新的元素item到棧頂
pop() 彈出棧頂元素
peek() 返回棧頂元素
is_empty() 判斷棧是否為空
size() 返回棧的元素個數
?
?
哈希
1、什么是哈希表
要說哈希表,我們必須先了解一種新的存儲方式—散列技術。
散列技術是指在記錄的存儲位置和它的關鍵字之間建立一個確定的對應關系f,使每一個關鍵字都對應一個存儲位置。即:存儲位置=f(關鍵字)。這樣,在查找的過程中,只需要通過這個對應關系f 找到給定值key的映射f(key)。只要集合中存在關鍵字和key相等的記錄,則必在存儲位置f(key)處。我們把這種對應關系f 稱為散列函數或哈希函數。
按照這個思想,采用散列技術將記錄存儲在一塊連續的存儲空間中,這塊連續的存儲空間稱為哈希表。所得的存儲地址稱為哈希地址或散列地址。
2、哈希表查找步驟
①、存儲數據時,將數據存入通過哈希函數計算所得哪那個地址里面。
②、查找時,使用同一個哈希函數通過關鍵字key計算出存儲地址,通過該地址即可訪問到查找的記錄。
3、哈希沖突
在理想的情況下,每一個 關鍵字,通過哈希函數計算出來的地址都是不一樣的。但是在實際情況中,我們常常會碰到兩個關鍵字key1≠key2,但是f(key1) = f(key2), 這種現象稱為沖突,并把key1和key2稱為這個散列函數的同義詞。
沖突的出現會造成查找上的錯誤,具體解決方法會在后文提到。
一種直觀的想法是如果沖突了我能不能讓數組中 對應的槽變成一個鏈式結構呢?這就是其中一種解決方法,叫做鏈接法(chaining)。如果我們用鏈接法來處理沖突,
這樣就用鏈表解決了沖突問題,但是如果哈希函數選不好的話,可能就導致沖突太多一個鏈變得太長,這樣查找就不再是 O(1) 的了。 還有一種叫做開放尋址法(open addressing),它的基本思想是當一個槽被占用的時候,采用一種方式來尋找下一個可用的槽。 (這里槽指的是數組中的一個位置),根據找下一個槽的方式不同,分為:
線性探查(linear probing): 當一個槽被占用,找下一個可用的槽。 h(k,i)=(h′(k)+i)%m,i=0,1,...,m?1
二次探查(quadratic probing): 當一個槽被占用,以二次方作為偏移量。 h(k,i)=(h′(k)+c1+c2i2)%m,i=0,1,...,m?1
雙重散列(double hashing): 重新計算 hash 結果。 h(k,i)=(h1(k)+ih2(k))%m
我們選一個簡單的二次探查函數 h(k,i)=(home+i2)%m,它的意思是如果 遇到了沖突,我們就在原始計算的位置不斷加上 i 的平方。
哈希函數
到這里你應該明白哈希表插入的工作原理了,不過有個重要的問題之前沒提到,就是 hash 函數怎么選? 當然是散列得到的沖突越來越小就好啦,也就是說每個 key 都能盡量被等可能地散列到 m 個槽中的任何一個,并且與其他 key 被散列到哪個槽位無關。 如果你感興趣,可以閱讀后邊提到的一些參考資料。視頻里我們使用二次探查函數,它相比線性探查得到的結果沖突會更少。
裝載因子(load factor)
如果繼續往我們的哈希表里塞東西會發生什么?空間不夠用。這里我們定義一個負載因子的概念(load factor),其實很簡單,就是已經使用的槽數比哈希表大小。 比如我們上邊的例子插入了 8 個元素,哈希表總大小是 13, 它的 load factor 就是 8/13≈0.62。當我們繼續往哈希表插入數據的時候,很快就不夠用了。 通常當負載因子開始超過 0.8 的時候,就要新開辟空間并且重新進行散列了。
重哈希(Rehashing)
當負載因子超過 0.8 的時候,需要進行 rehashing 操作了。步驟就是重新開辟一塊新的空間,開多大呢?感興趣的話可以看下 cpython 的 dictobject.c 文件然后搜索 GROWTH_RATE 這個關鍵字,你會發現不同版本的 cpython 使用了不同的策略。python3.3 的策略是擴大為已經使用的槽數目的兩倍。開辟了新空間以后,會把原來哈希表里 不為空槽的數據重新插入到新的哈希表里,插入方式和之前一樣。這就是 rehashing 操作。
遞歸
遞歸用一種通俗的話來說就是自己調用自己,但是需要分解它的參數,讓它解決一個更小一點的問題,當問題小到一定規模的時候,需要一個遞歸出口返回。
遞歸必須包含一個基本的出口(base case),否則就會無限遞歸,最終導致棧溢出。比如這里就是 n == 0 返回 1
遞歸必須包含一個可以分解的問題(recursive case)。 要想求得 fact(n),就需要用 n * fact(n-1)
遞歸必須必須要向著遞歸出口靠近(toward the base case)。 這里每次遞歸調用都會 n-1,向著遞歸出口 n == 0 靠近
計算機內部使用調用棧來實現遞歸,這里的棧一方面指的是內存中的棧區,一方面棧又是之前講到的后進先出這種數據結構。 每當進入遞歸函數的時候,系統都會為當前函數開辟內存保存當前變量值等信息,每個調用棧之間的數據互不影響,新調用的函數 入棧的時候會放在棧頂。
用棧模擬遞歸
剛才說到了調用棧,我們就用棧來模擬一把。之前棧這一章我們講了如何自己實現棧,不過這里為了不拷貝太多代碼,我們直接用 collections.deque 就可以 快速實現一個簡單的棧。
?
?
這里結果也是輸出 1 到 10,只不過我們是手動模擬了入棧和出棧的過程,幫助你理解計算機是如何實現遞歸的,是不是挺簡單!現在你能明白為什么上邊 print_num_recursive print_num_recursive_revserve 兩個函數輸出的區別了嗎?
尾遞歸
上邊的代碼示例(麻雀雖小五臟俱全)中實際上包含了兩種形式的遞歸,一種是普通的遞歸,還有一種叫做尾遞歸:
?
概念上它很簡單,就是遞歸調用放在了函數的最后。有什么用呢? 普通的遞歸, 每一級遞歸都產生了新的局部變量, 必須創建新的調用棧, 隨著遞歸深度的增加, 創建的棧越來越多, 造成爆棧。雖然尾遞歸調用也會創建新的棧, 但是我們可以優化使得尾遞歸的每一級調用共用一個棧!, 如此便可解決爆棧和遞歸深度限制的問題! 不幸的是 python 默認不支持尾遞歸優化(見延伸閱讀),不過一般尾遞歸我們可以用一個迭代來優化它。
漢諾塔問題
有三根桿子A,B,C。A桿上有N個(N>1)穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將所有圓盤移至C桿: 但是有兩個條件:
每次只能移動一個圓盤;
大盤不能疊在小盤上面。
最早發明這個問題的人是法國數學家愛德華·盧卡斯。 傳說越南河內某間寺院有三根銀棒,上串64個金盤。寺院里的僧侶依照一個古老的預言,以上述規則移動這些盤子;預言說當這些盤子移動完畢,世界就會滅亡。 這個傳說叫做梵天寺之塔問題(Tower of Brahma puzzle)。但不知道是盧卡斯自創的這個傳說,還是他受他人啟發。
理解這個問題需要我們一些思維上的轉換,因為我們正常的思維可能都是從上邊最小的盤子開始移動,但是這里我們從移動最底下的盤子開始思考。 假設我們已經知道了如何移動上邊的四個盤子到 B(pole2),現在把最大的盤子從 A -> C 就很簡單了。當把最大的盤子移動到 C 之后,只需要把 B 上的 4 個盤子從 B -> C 就行。(這里的 pole1, 2, 3 分別就是 A, B, C 桿)
問題是仍要想辦法如何移動上邊的 4 個盤子,我們可以同樣的方式來移動上邊的 4 個盤子,這就是一種遞歸的解法。 給定 n 個盤子和三個桿分別是 源桿(Source), 目標桿(Destination),和中介桿(Intermediate),我們可以定義如下遞歸操作:
把上邊的 n-1 個盤子從 S 移動到 I,借助 D 桿
把最底下的盤子從 S 移動到 D
把 n-1 個盤子從 I 移動到 D,借助 S
我們把它轉換成代碼:
def hanoi_move(n, source, dest, intermediate):
if n >= 1: # 遞歸出口,只剩一個盤子
hanoi_move(n-1, source, intermediate, dest)
print("Move %s -> %s" % (source, dest))
hanoi_move(n-1, intermediate, dest, source)
hanoi_move(3, 'A', 'C', 'B')
# 輸出,建議你手動模擬下。三個盤子 A(Source), B(intermediate), C(Destination)
Move A -> C
Move A -> B
Move C -> B
Move A -> C
Move B -> A
Move B -> C
Move A -> C
樹與樹算法
樹的概念
樹(英語:tree)是一種抽象數據類型(ADT)或是實作這種抽象數據類型的數據結構,用來模擬具有樹狀結構性質的數據集合。它是由n(n>=1)個有限節點組成一個具有層次關系的集合。把它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點:
每個節點有零個或多個子節點;
沒有父節點的節點稱為根節點;
每一個非根節點有且只有一個父節點;
除了根節點外,每個子節點可以分為多個不相交的子樹;
樹的術語
節點的度:一個節點含有的子樹的個數稱為該節點的度;
樹的度:一棵樹中,最大的節點的度稱為樹的度;
葉節點或終端節點:度為零的節點;
父親節點或父節點:若一個節點含有子節點,則這個節點稱為其子節點的父節點;
孩子節點或子節點:一個節點含有的子樹的根節點稱為該節點的子節點;
兄弟節點:具有相同父節點的節點互稱為兄弟節點;
節點的層次:從根開始定義起,根為第1層,根的子節點為第2層,以此類推;
樹的高度或深度:樹中節點的最大層次;
堂兄弟節點:父節點在同一層的節點互為堂兄弟;
節點的祖先:從根到該節點所經分支上的所有節點;
子孫:以某節點為根的子樹中任一節點都稱為該節點的子孫。
森林:由m(m>=0)棵互不相交的樹的集合稱為森林;
樹的種類
無序樹:樹中任意節點的子節點之間沒有順序關系,這種樹稱為無序樹,也稱為自由樹;
有序樹:樹中任意節點的子節點之間有順序關系,這種樹稱為有序樹;
二叉樹:每個節點最多含有兩個子樹的樹稱為二叉樹;
完全二叉樹:對于一顆二叉樹,假設其深度為d(d>1)。除了第d層外,其它各層的節點數目均已達最大值,且第d層所有節點從左向右連續地緊密排列,這樣的二叉樹被稱為完全二叉樹,其中滿二叉樹的定義是所有葉節點都在最底層的完全二叉樹;
平衡二叉樹(AVL樹):當且僅當任何節點的兩棵子樹的高度差不大于1的二叉樹;
排序二叉樹(二叉查找樹(英語:Binary Search Tree),也稱二叉搜索樹、有序二叉樹);
霍夫曼樹(用于信息編碼):帶權路徑最短的二叉樹稱為哈夫曼樹或最優二叉樹;
B樹:一種對讀寫操作進行優化的自平衡的二叉查找樹,能夠保持數據有序,擁有多余兩個子樹。
樹的存儲與表示
順序存儲:將數據結構存儲在固定的數組中,然在遍歷速度上有一定的優勢,但因所占空間比較大,是非主流二叉樹。二叉樹通常以鏈式存儲。
鏈式存儲:
由于對節點的個數無法掌握,常見樹的存儲表示都轉換成二叉樹進行處理,子節點個數最多為2
常見的一些樹的應用場景
1.xml,html等,那么編寫這些東西的解析器的時候,不可避免用到樹
2.路由協議就是使用了樹的算法
3.mysql數據庫索引
4.文件系統的目錄結構
5.所以很多經典的AI算法其實都是樹搜索,此外機器學習中的decision tree也是樹結構
轉載于:https://www.cnblogs.com/muzinan110/p/11157219.html
總結
以上是生活随笔為你收集整理的数据结构(python)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS----------iPhone导
- 下一篇: 转:IE iframe不刷新的问题之完美