【學(xué)習(xí)】python數(shù)據(jù)結(jié)構(gòu)和算法

https://www.bilibili.com/video/BV1VC4y1x7uv?p=85

二、算法分析

2.2 什么是算法分析
  • 大O表示法


    image.png
2.3 python數(shù)據(jù)結(jié)構(gòu)的性能
  • 列表


    image.png
  • 字典


    image.png

說一下list[index]的o(1)原理,dict是散列表形式,訪問函數(shù)是o(1)很容易理解。但實際上list的訪問函數(shù)我認(rèn)為也是一種“散列表”,或者直接是額外空間的賦值存在。即在原始的線性結(jié)構(gòu)(鏈表)外,為了能夠快速訪問(畢竟索引是最必須和高頻的函數(shù))而采用了額外空間來加速訪問。一種簡單的形式就是,比如list[1]用node_1來賦值,list[n]用node_n來賦值,需要訪問時,直接去找node_index就可以了,這是最簡單的方式。(書上的實現(xiàn)形式并不是python真正的實現(xiàn)形式)

三、基本數(shù)據(jù)結(jié)構(gòu)類型

3.2 線性結(jié)構(gòu)

包括棧、隊列、雙端隊列和列表。
棧:先進(jìn)后出
隊列:先進(jìn)先出
雙端隊列:允許先進(jìn)先出\先進(jìn)后出,典型場景是回文詞判定
列表:這里考慮無序列表需要的方法:

  • class node: node.getData, node.getNext, node.setData, node.setNext
  • class unorderdlist: unorderdlist.add, 其他就是常規(guī)函數(shù),isempty, search等。。

四、遞歸Recursion

4.2 什么是遞歸

遞歸三大定律

  • 必須有基本結(jié)束條件
  • 必須改變自己的狀態(tài)并向基本結(jié)束條件演進(jìn)
  • 必須遞歸地調(diào)用自身

典型問題1:謝爾賓斯基三角

  • 結(jié)束條件:剩余迭代次數(shù)>=1
  • 演進(jìn)和遞歸調(diào)用:
    1、把中間的三角形剔除;2、把左邊三角形進(jìn)行迭代;3、把上邊三角形進(jìn)行迭代;4、把右邊三角形迭代

典型問題2:河內(nèi)塔問題

  • 結(jié)束條件:小塔層數(shù)為1(只有一個圓盤)
  • 演進(jìn)和遞歸調(diào)用:
    1、把底盤上面的小塔從起點移到中間點,小塔進(jìn)行迭代;2、把底盤從起點移到終點;
    3、把底盤上面的小塔從中間點移到終點,小塔進(jìn)行迭代
4.5 動態(tài)規(guī)劃

典型問題:硬幣找零
先說一下貪心算法:先找面值最大的硬幣,剩下的零散的余額再用剩下的面值最大的硬幣來找零。。顯然在此場景下不是最優(yōu)解
動態(tài)規(guī)劃本質(zhì)上是一個遞歸問題:

def recDC(coinValueList,change,knownResults):
    minCoins = knownResults[change]  #在此場景下,最大的次數(shù)就是余額本身,所以這里相當(dāng)于是賦的初值為最大值
    if change in coinValueList:
        knownResults[change] = 1
        return 1
    if minCoins < change:
        return minCoins
    else:
        for i in [c for c in coinValueList if c <= change]:
            numCoins = 1 + recDC(coinValueList, change-i, knownResults)
            if numCoins < minCoins:
                minCoins = numCoins
                knownResults[change] = minCoins
        return minCoins

knownResults = list(range(64))
print(recDC([1,5,10,25],63,knownResults))
  • 設(shè)定一個初始列表,來保存每一個值條件下的最優(yōu)解,初值設(shè)為最差解
  • 依次對列表中的值進(jìn)行迭代,求解當(dāng)前值的最優(yōu)解:
    1、如果有一個明確的最優(yōu)解,那就是本身(此場景下就是對應(yīng)找零余額存在合適的硬幣);
    2、 沒有最優(yōu)解,則求解依次減去之前的值,再用新值去求最優(yōu)解,并用最后的復(fù)合答案最優(yōu)解,依次迭代;
    3、完全迭代后,用最優(yōu)解中的最優(yōu)解作為當(dāng)前值的最優(yōu)解。。。

五、排序和搜索

5.2 搜索
  • 順序搜索
    無序表直接進(jìn)行順序搜索,o(n)
  • 二分搜索
    無序表可以先進(jìn)行排序(額外消耗),再進(jìn)行有序列表的搜索,o(logn),在要進(jìn)行大量搜索的時候綜合效率最高。如果本身只執(zhí)行單次搜索,效果可能不如順序搜索
5.2.3 散列

  • 散列表的每一個位置是槽
  • 負(fù)載因子
    數(shù)據(jù)項個數(shù)/散列表大小
  • 沖突
  • 完美散列函數(shù)
    實際上并不需要完美散列函數(shù),而是用解決沖突的辦法來合理利用槽
  • 沖突解決方法
    線性探測:尋找開放地址,但容易產(chǎn)生集中趨勢,造成周邊一系列槽都被線性探測填充;
    二次探測法:不進(jìn)行線性探測,而是采用一個再散列函數(shù)來尋找新的槽
5.3 排序
  • 冒泡排序
    對列表相鄰兩項進(jìn)行比較,并交換順序確保后一項大于前一項,每一輪遍歷完成則有一個最大值排到最后的位置。o(n2)
  • 選擇排序
    每遍歷一次只交換一次數(shù)據(jù),即將最大項放到最后的位置。o(n2)
  • 插入排序
    依次將列表前方進(jìn)行排序,然后將后一項插入到前方已排序好的列表中正確位置。o(n2)
  • 希爾排序
    間隔插入排序,以插入排序為基礎(chǔ)。大致介于O(n)和O(n2)之間。
  • 歸并排序
    分而治之,持續(xù)地將一個列表分成兩半。如果列表是空的或者
    只有一個元素,那么根據(jù)定義,它就被排序好了(最基本的情況)。如果列表里的元素超過一個,我們就把列表拆分,然后分別對兩個部分調(diào)用遞歸排序。一旦這兩個部分被排序好了,那么就是歸并好了。O(nlogn),效率是最高的,但是需要額外的空間來進(jìn)行兩個歸并數(shù)組的比較。
  • 快速排序
    同樣是分而治之,選擇一個中值,并確定好中值在列表中正確的位置;將中值的左列表和右列表進(jìn)行中值迭代排序。O(nlogn)~O(n2)之間,但不需要額外的存儲空間。

六、樹和樹算法

6.2 術(shù)語
  • 節(jié)點:又可細(xì)分為根節(jié)點、子節(jié)點、葉節(jié)點(沒有子節(jié)點的節(jié)點)、父節(jié)點、兄弟節(jié)點
  • 子樹
  • 層數(shù)(根節(jié)點層數(shù)為0),高度(所有層數(shù)的最大值)
  • 路徑、邊
6.4 嵌套列表來實現(xiàn)樹
myTree = ['a', ['b', ['d',[],[]], ['e',[],[]] ], ['c', ['f',[],[]], []] ]
image.png

所以對樹的class可以基于列表實現(xiàn)

6.7 樹的遍歷
  • 前序遍歷:先根后左再右
  • 中序遍歷:先左后根再右
  • 后序遍歷:先左后右再根
image.png

遍歷的方式?jīng)Q定了讀樹的方式,比如上面這一棵解析樹,只有中序遍歷是對的,(7+3)*(5-2)

6.8 二叉堆/二叉樹(優(yōu)先隊列)

除了先進(jìn)先出、先進(jìn)后出、雙端隊列之外,二叉堆可以實現(xiàn)特定的優(yōu)先隊列先出。二叉樹的關(guān)鍵是保持根節(jié)點是最小值,但是對其他節(jié)點不進(jìn)行排序。
完全二叉樹,是指左子樹和右子樹相對平衡,最多只相差一層的二叉樹。
生成完全二叉樹的復(fù)雜度是o(n),理論上對列表進(jìn)行排序的復(fù)雜度是o(n2),但是二叉樹只是保持最小值在根節(jié)點,有點類似于在無序列表中尋找最小值的復(fù)雜度是o(n)。

6.9 二叉搜索樹/BTS搜索樹

定義:始終保持左節(jié)點<父節(jié)點<右節(jié)點
bts搜索樹在極端情況下就一棵樹到底,因此更廣泛采用平衡二叉樹

6.10 平衡二叉樹/AVL樹

AVL樹在生成樹的時候會引入平衡因子+旋轉(zhuǎn)的操作來使得整體始終保持平衡

image.png

仿佛這里有問題,排好序的列表put和del的復(fù)雜度應(yīng)該也是log2(n)才對,按照二分法來定位

七、圖和圖算法

7.2 概念
  • 頂點
  • 權(quán)重
  • 路徑
7.3 實現(xiàn)形式
  • 鄰接矩陣(稀疏度通常特別大)
  • 鄰接表(更常用)
7.7 廣度優(yōu)先搜索(BFS)

詞梯問題:尋找由fool到sage最短的路徑


7.8 深度優(yōu)先搜索(DFS)

騎士周游問題(下圖并不代表實際問題):


image.png

每個節(jié)點記錄兩個值:發(fā)現(xiàn)時間和結(jié)束時間,典型特性是返回時間更晚的(父節(jié)點)優(yōu)先級總是要高于返回時間更早的(子節(jié)點),所以要實現(xiàn)深度搜索(所有節(jié)點都必須覆蓋),就得先通過父節(jié)點,再通過子節(jié)點。。適用于各種需要優(yōu)先級排序的場景,比如制作一個煎餅


image.png
7.11 dijkstra(BFS)

對于需要考慮權(quán)重的廣度搜索問題,dijkstra算法:每一個節(jié)點記錄源節(jié)點到當(dāng)前節(jié)點的最短路徑

7.12 prim(DFS)

對于需要走遍所有節(jié)點,但是為了保證路徑代價最小的問題,prim算法:每一個節(jié)點記錄源節(jié)點到當(dāng)前已發(fā)現(xiàn)樹(包含多個已發(fā)現(xiàn)節(jié)點)的最短路徑。。。走到最后一個節(jié)點的時候,記錄的就是源節(jié)點要走遍所有節(jié)點所需要的代價。。(書中的圖不對,每個節(jié)點記錄的值仍然是dijkstra)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容