b+树时间复杂度_数据结构:线性表,栈,队列,数组,字符串,树和二叉树,哈希表...
作者:張人大
代碼效率優化
復雜度 -- 一個關于輸入數據量n的函數
- 時間復雜度 -- 昂貴
- 與代碼的結構設計有著緊密關系
- 一個順序結構的代碼,時間復雜度是O(1), 即任務與算例個數 n 無關
- 空間復雜度 -- 廉價
- 與數據結構設計有關
數據結構 -- 考慮如何去組織計算機中一定量的數據。
數據結構連接時空,用空間換取時間。
數據處理 -- 了解問題,明確數據操作方法,設計出更加高效的數據結構類型
- 找到需要處理的數據,計算結果,再把結果保存下來
- 把結果存到新的內存空間中
- 把結果存到已使用的內存空間中
- 基本操作只有三個:增,刪,查
- 增和刪可以細分為數據結構的中間以及最后的增和刪
- 查找可以細分為按照位置條件查找和數據數值特征查找
- 所有數據處理都是這些基本操著的組合和疊加
- 只有字典類型數據結構能在 O(1) 的時間復雜度內完成查找動作
- 回歸問題本源,明確數據被處理的動作,來解決數據結構的問題
想了解更多,歡迎關注我的微信公眾號:Renda_Zhang
線性表
n 個具有相同特性的元素的有限序列,Linear List
數據元素之間的關系是一對一的關系
- 即除了頭尾元素外,其它數據元素都是首尾相接的
- 這句話只適用大部分線性表,而不是全部
- 比如,循環鏈表尾的指針指向首位結點
實現方式
- 最常用的是鏈式表達,也叫線性鏈表或鏈表
- 每個結點包括具體的數據值和指向下一個結點的指針
- 單向鏈表,循環鏈表,雙向鏈表,雙向循環鏈表
- 新增和刪除為 O(1) 時間復雜度,而查找為 O(n)
- 適合數據元素個數不確定,且經常進行新增和刪除
- 鏈表的翻轉,快慢指針的方法,是必須掌握的內容
- 使用數組實現,也叫順序存儲,順序表
類別
- 一般線性表,可以自由的刪除和添加結點
- 受限線性表,主要包含棧和隊列
棧和隊列是特殊的線性表,本質上他們都可以被看作是一類基本結構
線性表案例
- 鏈表的翻轉
- 快慢指針
- 查找奇數個數的鏈表的中間位置結點的數值
- 判斷鏈表是否有環
棧
后進先出的(限制后的)線性表,Last In First Out, Stack.
新增和刪除操作只能在這個線性表的表尾進行,即在線性表基礎上加了限制
- 新增: 壓棧 push, which adds an element to the collection
- 刪除: 出棧 pop, which removes the most recently added element
功能上,數組或者鏈表可以代替棧,但它們靈活性過高,數據量大時有風險
棧頂和棧底是用來表示這個棧的兩個指針
- 棧頂 (top) 是表尾,用來輸入數據
- 棧底 (bottom) 是表頭
棧有順序表示和鏈式表示,分別稱作順序棧和鏈棧
- 順序棧
- 可以借助數組來實現
- 數組的首元素存在棧底,尾元素放在棧頂
- 定義指針 top 來指示棧頂元素在數組的位置
- 棧中只有一個元素,則 top = 0
- 以 top 是否為 -1 來判定是否為空棧
- 棧頂 top 需小于棧的最大容量
- 出棧操作,只需要 top - 1 即可
- 鏈式棧
- 用鏈表的方式實現
- 通常把棧頂放在單鏈表的頭部
- top 指針替換了鏈表原來的尾指針,去掉了頭指針
- 出棧操作,將 top 指針指向棧頂元素的 next 指針即可
- 對比棧和一般線性表
- 相同點:
- 操作原理相似
- 時間復雜度一樣
- 都依賴當前位置指針進行數據對象的操作
- 區別:棧只能新增和刪除棧頂的數據結點
棧的案例
- 判斷括號字符串是否合法
- 瀏覽器頁面訪問的后退和前進
隊列
先進先出 (限制后的) 線性表, First In First Out, Queue
新增和刪除操作只能分別在隊尾和隊頭進行
- 先進 - 隊列的數據新增操作只能在末端進行, add
- 不允許在隊列的中間某個結點后新增數據
- 先出 - 隊列的數據刪除操作只能在始端進行, remove
- 不允許在隊列的中間某個結點后刪除數據
隊列適合面對數據處理順序非常敏感的問題
- 可以確定隊列長度最大值, 建議使用循環隊列
- 無法確定隊列長度時, 應考慮使用鏈式隊列
front 和 rear 兩個指針
- 隊頭 (front), 用來刪除數據
- 隊尾 (rear), 用來增加數據
隊列有兩種存儲方式, 即順序隊列和鏈式隊列
- 順序隊列
- 依賴數組來實現
- 數據在內存中也是順序存儲
- 進行新增插入操作時,
- 尾指針會向后移動
- 時間復雜度為 O(1)
- 如果只刪除頭的第一個元素時
- 每次刪除都需要把整個數組前移
- 時間復雜度為 O(n)
- 使用循環隊列
- 必須有一個固定的長度
- 實現刪除的時間復雜度為 O(1)
- 使用 flag 來判斷隊列空或滿
- 鏈式隊列
- 依賴鏈表來實現
- 數據依賴每個結點的指針互聯
- 是離散存儲線性結構
- 實際上就是尾進頭出的單鏈線性表
- 在空間上更為靈活
- 通常會增加一個頭結點
- 讓 front 指針指向頭結點
- 頭結點不存儲數據, 只是輔助標識
- 當進行數據刪除時, 實際刪除的是頭結點的后繼結點
- 隊列為空時, 頭尾指針都指向頭結點
- 對比隊列和一般線性表
- 隊列繼承了線性表的優點和不足
- 是加了限制的線性表
隊列案例
- 約瑟夫環 - Josephus problem
數組
數組可以看成是線性表的一種推廣,它屬于另外一種基本的數據結構
數組是數據結構中的最基本結構
- 幾乎所有的程序設計語言都把數組類型設定為固定的基礎變量類型。
- 可以把數組理解為一種容器,它可以用來存放若干個相同類型的數據元素。
- 例如:
- 存放的數據是整數型的數組,稱作整型數組;
- 存放的數據是字符型的數組,則稱作字符數組;
- 另外還有一類數組比較特殊,它是數組的數組,也可以叫作二維數組。
- 可以把普通的數組看成是一個向量,那么二維數組就是一個矩陣。
- 數組在內存中是連續存放的,數組內的數據,可以通過索引值直接取出得到。
數組的索引就是對應數組空間
- 在進行新增、刪除、查詢操作的時候,完全可以根據代表數組空間位置的索引值進行。
- 只要記錄該數組頭部的第一個數據位置,然后累加空間位置即可。
數組的基本操作
具有增刪困難、查找容易的特點,可以在任意位置增刪數據,所以數組的增刪操作會更為多樣。
- 新增操作
- 若插入數據在最后,則時間復雜度為 O(1)
- 如果中間某處插入數據,則時間復雜度為 O(n)
- 刪除操作
- 在數組的最后刪除一個數據元素,則時間復雜度是 O(1)
- 在這個數組的中間某個位置刪除一條數據, 時間復雜度為 O(n)
- 查找操作
- 如果只需根據索引值進行一次查找,時間復雜度是 O(1)
- 要在數組中查找一個數值滿足指定條件的數據,則時間復雜度是 O(n)。
對比數組和鏈表
- 鏈表的長度是可變的,數組的長度是固定的,在申請數組的長度時就已經在內存中開辟了若干個空間。如果沒有引用 ArrayList 時,數組申請的空間永遠是我們在估計了數據的大小后才執行,所以在后期維護中也相當麻煩。
- 鏈表不會根據有序位置存儲,進行插入數據元素時,可以用指針來充分利用內存空間。數組是有序存儲的,如果想充分利用內存的空間就只能選擇順序存儲,而且需要在不取數據、不刪除數據的情況下才能實現。
數組的案例
- 基于數組,計算平均值
字符串
由 n 個字符組成的一個有序整體( n >= 0 )
對比字符串和線性表
- 字符串的邏輯結構和線性表極為相似,區別僅在于串的數據對象約束為字符集。
- 字符串的基本操作和線性表有很大差別:
- 在線性表的基本操作中,大多以“單個元素”作為操作對象;
- 在字符串的基本操作中,通常以“串的整體”作為操作對象;
- 字符串的增刪操作和數組很像,復雜度也與之一樣。但字符串的查找操作就復雜多了,它是參加面試、筆試常常被考察的內容。
特殊的字符串
- 空串,指含有零個字符的串。例如,s = "",書面中也可以直接用 ? 表示。
- 空格串,只包含空格的串。它和空串是不一樣的,空格串中是有內容的,只不過包含的是空格,且空格串中可以包含多個空格。例如,s = " ",就是包含了 3 個空格的字符串。
- 子串,串中任意連續字符組成的字符串叫作該串的子串。
- 原串通常也稱為主串。
字符串的存儲結構與線性表相同,也有順序存儲和鏈式存儲兩種
- 字符串的順序存儲結構,是用一組地址連續的存儲單元來存儲串中的字符序列,一般是用定長數組來實現。有些語言會在串值后面加一個不計入串長度的結束標記符,比如 0 來表示串值的終結。
- 字符串的鏈式存儲結構,與線性表是相似的,但由于串結構的特殊性(結構中的每個元素數據都是一個字符),如果也簡單地將每個鏈結點存儲為一個字符,就會造成很大的空間浪費。因此,一個結點可以考慮存放多個字符,如果最后一個結點未被占滿時,可以使用 "#" 或其他非串值字符補全。
- 每個結點設置字符數量的多少,與串的長度、可以占用的存儲空間以及程序實現的功能相關。
- 除了在連接串與串操作時有一定的方便之外,不如順序存儲靈活,在性能方面也不如順序存儲結構好。
字符串的基本操作
- 新增操作
- 和數組非常相似,都牽涉對插入字符串之后字符的挪移操作,所以時間復雜度是 O(n)。
- 對于特殊的插入操作時間復雜度也可以降低為 O(1)。例如,在 s1 的最后插入 s2,也叫作字符串的連接。
- 刪除操作
- 和數組同樣非常相似,也可能會牽涉刪除字符串后字符的挪移操作,所以時間復雜度是 O(n)。
- 對于特殊的刪除操作時間復雜度也可以降低為 O(1)。例如,在 s1 的最后刪除若干個字符,不牽涉任何字符的挪移。
- 查找操作
- 子串查找(字符串匹配)
- 在字符串 A 中查找字符串 B,則 A 就是主串,B 就是模式串。
- 主串的長度記為 n,模式串長度記為 m,則n>m。
- 字符串匹配算法的時間復雜度就是 n 和 m 的函數。
字符串匹配算法的案例
- 查找出兩個字符串的最大公共字串
樹和二叉樹
樹 -- Tree
- 樹結構在存在“一對多”的數據關系中,可被高頻使用,這也是它區別于鏈表系列數據結構的關鍵點。
- 樹是由結點和邊組成的,不存在環的一種數據結構。
- 樹滿足遞歸定義的特性。如果一個數據結構是樹結構,那么剔除掉根結點后,得到的若干個子結構也是樹,通常稱作子樹。
- 樹的結點的層次從根結點算起,根為第一層,根的“孩子”為第二層,根的“孩子”的“孩子”為第三層,依此類推。
- 樹中結點的最大層次數,就是這棵樹的樹深(稱為深度,也稱為高度)。
二叉樹 -- Binary Tree
二叉樹每個結點最多有兩個子結點,分別稱作左子結點和右子結點。
二叉樹中兩個特殊的類型
- 滿二叉樹,定義為除了葉子結點外,所有結點都有 2 個子結點。
- 完全二叉樹,定義為除了最后一層以外,其他層的結點個數都達到最大,并且最后一層的葉子結點都靠左排列。它方便了順序存儲法的存儲方式。
存儲二叉樹的兩種辦法
- 鏈式存儲法,也就是像鏈表一樣,每個結點有三個字段,一個存儲數據,另外兩個分別存放指向左右子結點的指針。
- 順序存儲法,就是按照規律把結點存放在數組里。如圖所示。
樹的基本操作
遍歷
- 前序遍歷,對樹中的任意結點來說,先打印這個結點,然后前序遍歷它的左子樹,最后前序遍歷它的右子樹。 public static void preOrderTraverse(Node node) { if (node == null) return; System.out.print(node.data + " "); preOrderTraverse(node.left); preOrderTraverse(node.right); }
- 中序遍歷,對樹中的任意結點來說,先中序遍歷它的左子樹,然后打印這個結點,最后中序遍歷它的右子樹。 public static void inOrderTraverse(Node node) { if (node == null) return; inOrderTraverse(node.left); System.out.print(node.data + " "); inOrderTraverse(node.right); }
- 后序遍歷,對樹中的任意結點來說,先后序遍歷它的左子樹,然后后序遍歷它的右子樹,最后打印它本身。 public static void postOrderTraverse(Node node) { if (node == null) return; postOrderTraverse(node.left); postOrderTraverse(node.right); System.out.print(node.data + " "); }
二叉樹的增刪查操作很普通,時間復雜度與鏈表并沒有太多差別
二叉查找樹 -- Binary Search Tree, BST
特性
- 在二叉查找樹中的任意一個結點,其左子樹中的每個結點的值,都要小于這個結點的值。
- 在二叉查找樹中的任意一個結點,其右子樹中每個結點的值,都要大于這個結點的值。
- 在二叉查找樹中,會盡可能規避兩個結點數值相等的情況。
- 對二叉查找樹進行中序遍歷,就可以輸出一個從小到大的有序數據隊列。
查找操作 -- 利用了“二分查找”,所消耗的時間復雜度為 O(logn)。
- 首先判斷根結點是否等于要查找的數據,如果是就返回。
- 如果根結點大于要查找的數據,就在左子樹中遞歸執行查找動作,直到葉子結點。
- 如果根結點小于要查找的數據,就在右子樹中遞歸執行查找動作,直到葉子結點。
插入操作
- 插入操作很簡單。從根結點開始,如果要插入的數據比根結點的數據大,且根結點的右子結點不為空,則在根結點的右子樹中繼續嘗試執行插入操作。直到找到為空的子結點執行插入動作。
- 二叉查找樹插入數據的時間復雜度是 O(logn)。這里的時間復雜度更多是消耗在了遍歷數據去找到查找位置上,真正執行插入動作的時間復雜度仍然是 O(1)。
刪除操作
- 情況一,如果要刪除的結點是某個葉子結點,則直接刪除,將其父結點指針指向 null 即可。
- 情況二,如果要刪除的結點只有一個子結點,只需要將其父結點指向的子結點的指針換成其子結點的指針即可。
- 情況三,如果要刪除的結點有兩個子結點,則有兩種可行的操作方式:
- 第一種,找到這個結點的左子樹中最大的結點,替換要刪除的結點。
- 第二種,找到這個結點的右子樹中最小的結點,替換要刪除的結點。
樹的案例
字典樹 -- Dictionary Tree
- 第一,根結點不包含字符;
- 第二,除根結點外每一個結點都只包含一個字符;
- 第三,從根結點到某一葉子結點,路徑上經過的字符連接起來,即為集合中的某個字符串。
哈希表
哈希表 -- Hash Table, 也叫作散列表。
哈希表是一種特殊的數據結構,它與數組、鏈表以及樹等我們之前學過的數據結構相比,有很明顯的區別。
- 線性表中的棧和隊列對增刪有嚴格要求,它們會更關注數據的順序。
- 數組和字符串需要保持數據類型的統一,并且在基于索引的查找上會更有優勢。
- 樹的優勢則體現在數據的層次結構上。
- 哈希表優勢體現在,無論有多少數據,查找、插入、刪除只需要接近常量的時間,即 O(1)的時間級。
核心思想
實現 “地址 = f (關鍵字)” 的映射關系,快速完成基于數據的數值的查找。
哈希函數的設計
直接定制法
哈希函數為關鍵字到地址的線性函數。如,H (key) = a*key + b。 這里,a 和 b 是設置好的常數。
數字分析法
假設關鍵字集合中的每個關鍵字 key 都是由 s 位數字組成(k1,k2,…,Ks),并從中提取分布均勻的若干位組成哈希地址。
平方取中法
如果關鍵字的每一位都有某些數字重復出現,并且頻率很高,我們就可以先求關鍵字的平方值,通過平方擴大差異,然后取中間幾位作為最終存儲地址。
折疊法
如果關鍵字的位數很多,可以將關鍵字分割為幾個等長的部分,取它們的疊加和的值(舍去進位)作為哈希地址。
除留余數法
預先設置一個數 p,然后對關鍵字進行取余運算。即地址為 key mod p。
解決哈希沖突
開放定址法
常用的探測方法是線性探測法。比如有一組關鍵字 {34,35,36,45},采用的哈希函數為 key mod 11。當插入 34,35,36 時可以直接插入,地址分別為 1、2、3。而當插入 45 時,哈希地址為 45 mod 11 = 1。然而,地址 1 已經被占用,因此沿著地址 1 依次往下探測,直到探測到地址 4,發現為空,則將 45 插入其中。
鏈地址法
將哈希地址相同的記錄存儲在一張線性鏈表中。如果出現沖突,就在對應的位置上加上鏈表的數據結構。
哈希表的基本操作
哈希表中的增加和刪除數據操作,不涉及增刪后對數據的挪移問題
- 如果是采用數組實現就需要考慮數據的挪移問題
哈希表查找的細節過程是:對于給定的 key,通過哈希函數計算哈希地址 H (key)。
- 如果哈希地址對應的值為空,則查找不成功。
- 反之,則查找成功。
哈希表的案例
實時返回用戶的字符串搜索結果
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的b+树时间复杂度_数据结构:线性表,栈,队列,数组,字符串,树和二叉树,哈希表...的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: python淘宝cookies抢购_Py
- 下一篇: python文件读写用到的库_Pytho
