php tree(九)資料結構/ 演算法
阿新 • • 發佈:2018-11-26
資料結構
- 常見資料結構與演算法整理總結(上)
- 線性表(陣列,連結串列),棧與佇列,樹,二叉樹,二叉查詢/搜尋/排序樹,平衡二叉樹,紅黑樹,圖。
- 陣列和連結串列
型別 分配記憶體方式 記憶體連續性 位置 查詢複雜度 插入/刪除 陣列 靜態 連續 - 優點:可以通過下標來訪問元素
- 缺點:若插入/刪除元素,要將其後所有元素移動位置
棧 O(1) O(n) 連結串列 動態 不連續 堆 O(n) O(1) - 堆和棧。佇列、堆、棧、堆疊的區別?
佇列 有一個入口和一個出口,先進先出 棧 就像一個箱子,後進先出 堆 請求作業系統分配記憶體,效率低
- 資料結構中常用的樹
遍歷 都是相對於根節點來說的,左節點總是在右節點前面 - 先序:根-左-右
- 中序:左-根-右
- 後序:左-右-根
階 表示一個節點最多能有多少個子節點,如二叉樹的階數就是2。 度 指父結點下面有幾個孩子結點。 滿二叉樹 一棵深度為k且有2k-1(2的k次冪減1)個結點的二叉樹稱為滿二叉樹。 完全二叉樹 每一個結點都與深度為k的滿二叉樹中編號從1至n的結點一一對應。 二叉樹 每個結點至多隻有2棵子樹,二叉樹的子樹有左右之分,次序不能顛倒。 - 型別:滿二叉樹,完全二叉樹。
- 二叉樹第 i 層上最多有2i-1個節點
- 深度為 k 的二叉樹,最多有2k - 1個節點
- 對於任意一顆二叉樹T,如果其終端節點數為n1度數為2的節點數為n2 則 n1 = n2 + 1
- 具有n個節點的完全二叉樹深度為[ log2n] + 1;
樹和二叉樹 - 二叉樹每個節點最多有2個子節點,樹無限制。
- 二叉樹有序。子樹分為左子樹和右子樹,即使某節點只有一棵子樹,也要指明是左子樹還是右子樹。
- 二叉樹可以是空。樹不能為空,至少有一個節點。
- BST二叉搜尋樹、AVL平衡二叉樹、RBT紅黑樹、B-樹、B+樹、B*樹。AVL 紅黑樹 B(+)樹 跳錶 字典樹 應用場景及分析。
型別 特點 缺點 應用 二叉查詢樹 - 節點有序,利於二分查詢。
- 左子樹小於它的根結點,右子樹大於它的根結點,沒有值相等的結點。
- 因為不一定平衡,有可能退化成線性表,搜尋效能不佳
- 用於搜尋
平衡二叉樹 - 是二叉查詢樹
- 嚴格要求平衡。左子樹和右子樹的深度之差的絕對值不超過1。
- 查詢效能好,維護成本高
- 適合用於插入刪除次數比較少,但查詢多的情況。
紅黑樹 - 通過任何一條從根到葉子的簡單路徑上各個節點的顏色進行約束,確保沒有一條路徑會比其他路徑長2倍,因而是近似平衡的。
- 相對於AVL樹來說,旋轉保持平衡次數較少。用於搜尋時,插入刪除次數多的情況下我們使用紅黑樹來取代AVL。
- 更新資料時,紅黑樹有個平衡的過程,牽涉到大量的節點,爭鎖的代價相對高。效能不如跳躍表。
- epoll 在核心中的實現,用紅黑樹來管理事件塊
- nginx中,用紅黑樹來管理timer等
- Java中的TreeMap實現
B樹/B+樹 - 多路查詢樹,分支多層數少,可以有效減少磁碟IO次數。
- 相當於AVL更適合於檔案系統。
null - 磁碟IO是非常耗時的,為檔案系統、資料庫系統而生。
跳躍表 - 更新資料時,跳躍表需要更新的部分少,鎖的東西少,效能佳
null - redis sorted set
- 什麼是B-Tree。什麼是B+Tree。
資料庫索引 - 索引儲存在磁碟上,資料量比較大時,索引大小也跟著增長,達到幾個G。
- 利用索引進行查詢時,不可能把索引全部載入到記憶體,只能逐一載入每個磁碟頁,這裡的磁碟頁就對應索引樹的節點。
b樹 - 關鍵字集合分佈在整顆樹中;
- 任何一個關鍵字出現且只出現在一個結點中;
- 搜尋有可能在非葉子結點結束;
- 其搜尋效能等價於在關鍵字全集內做一次二分查詢;
- 自動層次控制
b+樹 - 單節點可以儲存更多的元素。使得查詢磁碟IO次數更少。
- 所有查詢都要查詢到葉子節點,查詢效能穩定。
- 所有葉子節點形成有序連結串列,便於範圍查詢。
- 資料結構之雜湊表。大多數分散式儲存系統要麼實現一個分散式Hash表,要麼實現分散式B+樹。
- Redis為什麼用跳錶而不用平衡樹?
- 跳躍表特點
- 在有序連結串列的基礎上發展而來的多層連結串列。
- 間隔一定元素增加指標,原連結串列足夠長時,能跳過很多下層節點,大大加快查詢速度。
- 上層連結串列節點個數,是下曾節點個數一半,非常類似一個二分查詢。時間複雜度O(log n)。
- 然後對於刪除和插入,採用隨機生成層數,保證插入或者刪除只需要修改結點前後指標,效能優於平衡樹。
- 層隨機數,不超過一個最大值。
- 對比
型別 元素是否有序 平均複雜度/ 單key查詢 範圍查詢 記憶體佔用 演算法實現難度 優點 平衡樹 是 O(log n) 複雜 複雜 平衡 雜湊表 否。所以不支援範圍查詢 O(1) 不支援 大(需要事先分配足夠的記憶體儲存散列表) 取決於雜湊函式 查詢快 跳躍表 是 O(log n) 簡單 靈活 簡單 更新資料時,跳躍表需要更新的部分少,鎖的東西少 - sorted set資料結構實現
- 資料較少時,由ziplist實現。
- 資料較多時,由dict + skiplist實現。dict維護資料到分數的對應,skiplist用來根據分數範圍查詢資料。
- 分數相同時,根據資料內容進行字典排序。
- 第一層量表不是單向連結串列,而是一個雙向連結串列,為了方便以倒序方式獲取一個範圍內元素。只有第一層連結串列時雙向連結串列。
- 跳躍表特點
演算法
- 常見資料結構與演算法整理總結(下)
- 常見排序演算法。八大排序演算法。
大類 小類 演算法 選擇 選擇 - 每個數(當前數)和之後的每個數比較,每次記下小數的位置,然後交換當前數和最小數的位置
堆排序 特點: - 堆是具有以下性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱為大頂堆;
- 或者每個結點的值都小於或等於其左右孩子結點的值,稱為小頂堆。
- 若升序,構建大頂堆,
- 堆頂元素和末尾元素交換,末尾表示排好的序列。
- 對前面元素重複前2步,
- 直到整個序列有序。
插入 直接插入 - 從第一個數開始,之後每個數插入前面假定排好序的位置。根據如何確定插入位置,分為直接插入(遍歷)和二分查詢插入
希爾排序 改進的插入排序。按步長進行分組,每組內的元素進行插入排序。逐步減小步長,直到為1,即為普通的插入排序的情況。前面的步驟可以將所有元素變成基本是排好序的,較少要移動的元素。 交換 冒泡 - 每個數與下一個相鄰的數比較,大的往後放。每輪下來,產生一個最大數放在最後位置(前插在後面最大數序列中)
快速 - 選擇基準數(第一個也可以),比它大的放右邊容器,小的放左邊,左邊和右邊數做同樣處理(遞迴),然後合併左、基準數、右邊數即得
歸併排序 - 分而治之
基數排序 按個位大小排序、按十位大小排序、百位...... 總結 平均時間複雜度:nlogn:快速、堆、歸併。關鍵字隨機分佈時,快速排序平均時間最短 - 查詢
順序查詢 二分查詢 - 兩個名字(二分查詢、折半查詢)
- 優點三個(比較次數少、查詢速度快、平均效能好)
- 缺點兩個(待查詢表為有序表、插入刪除困難),用數字表示就是232。8
- 初始傳入待查詢陣列的左右邊界分別為0和陣列長度-1。
- 邊界檢查:如果左邊界>右邊界,則返回“找不到該數”,退出,
- 將待查詢數與中間位置(向下取整,只要能定出界限,無所謂多1還是少1)數比較,
- 如果比中間位置數大,則繼續在中間位置右邊的數中查詢(執行遞迴,左邊界變成中間位置+1),
- 如果比中間位置數小,則繼續在中間位置左邊的數中查詢(執行遞迴,右邊界變成中間位置-1),
- 否則相等的話,表示找到待查詢的數,退出。
- 程式碼實現:遞迴與非遞迴實現
分塊查詢 是前兩者結合,分塊有序,塊內無序,插入和刪除無需大量移動記錄 散列表 - 程式碼:常用排序和兩個查詢。
- 實戰:
- 劍指offer中的演算法題。資料結構和演算法面試題。
- 百錢買雞。滿足一定條件,資料在一定範圍。制定for迴圈,剩下的事交給計算機。制定等式,接下來思路,讓變數變化,所以制定for迴圈。
- 約瑟夫環。遞迴演算法和公式法。
- 斐波那契數列。遞迴法和迭代法。
- TopK。bitmap計數,求TopK最快的方法?
常規 - 全域性排序,再取前k個。複雜度:n*lg(n)(快速排序)。
- 區域性排序,只對最大k個排序。使用冒泡排出k個最大數。複雜度:n*k(冒泡:n*n)。
- 堆排序。只找到TopK,不排序TopK。複雜度:n*lg(k)(堆排序:n*lg(n))。
- 分治,每個分支都要遞迴,例如快速排序。不懂。
- 減治,只要遞迴一個分支,例如二分查詢。不懂。
- 隨機選擇+partition。不懂。
bitmap 元素沒有重複 - 相應位的元素存在則存1,否則存0。
- 掃描一次所有n個元素,生成bitmap,時間複雜度O(n)。
- 生成後,去TopK只需要找到最高位的k個bit即可。
- 每個元素的一個bit變成一個計數,
- 找TopK的過程:從高位往低位掃描,得到count之和等於k,對應的bit就是TopK所求。
- 全排列演算法。
- 天平稱球。
- 二進位制和三進位制的妙用。
- PHP實現的演算法合集。