4.B-樹

一、常見的查找方式?

順序查找 O(N)

二分查找 O(logN)(要求有序和隨機訪問)

二叉搜索樹 O(N)

平衡二叉搜索樹(AVL樹和紅黑樹) O(logN)

哈希 O(1)
考慮效率和要求而言,正常選用 平衡二叉搜索樹哈希 作為查找方式。

但這兩種結構適合用于數據量相對不是很大,能夠一次性存放在內存中,進行數據查找的場景。如果數據量很大,比如有100G數據,無法一次放進內存中,那就只能放在磁盤上了。

如果放在磁盤上,有需要搜索某些數據,那么如果處理呢?

可以考慮將?存放關鍵字及其映射的數據的地址放到一個內存中的搜索樹的節點?中。需要訪問數據時,先取這個地址去磁盤訪問數據。

分析效率的缺陷:

使用平衡二叉樹搜索樹的缺陷:
平衡二叉樹搜索樹的高度是 logN ,這個查找次數在內存中是很快的。但是當數據都在磁盤中時,訪問磁盤速度很慢,在數據量很大時,logN 次的磁盤訪問,是一個難以接受的結果。
使用哈希表的缺陷:
哈希表的效率很高是 O(1) ,但是一些極端場景下某個位置沖突很多,導致訪問次數劇增,也是難以接受的。
那如何加速對數據的訪問呢?
1. 提高 IO 的速度 (SSD 相比傳統機械硬盤快了不少,但是還是沒有得到本質性的提升 )
2. 降低樹的高度 --- 多叉樹平衡樹

二、B樹概念

1970 年, R.Bayer E.mccreight 提出了一種適合外查找的樹,它是一種平衡的多叉樹,稱為 B 樹。
m (m>2) B 樹,是一棵平衡的 M 路平衡搜索樹,可以是空樹 或者滿足一下性質:
1. 根節點至少有兩個孩子
2. 每個分支節點都包含 k-1 個關鍵字和 k 個孩子,其中 ceil(m/2) ≤ k ≤ m ?(ceil 是向上取整函數)
3. 每個葉子節點都包含 k-1 個關鍵字,其中 ceil(m/2) ≤ k ≤ m
4. 所有的葉子節點都在同一層
5. 每個節點中的關鍵字從小到大排列,節點當中 k-1 個元素正好是 k 個孩子包含的元素的值域劃 分。
6. 每個結點的結構為:( n A0 K1 A1 K2 A2 Kn An 其中, Ki(1≤i≤n) 為關鍵字,且Ki<Ki+1(1≤i≤n-1) Ai(0≤i≤n) 為指向子樹根結點的指針。且 Ai 所指子樹所有結點中的 關鍵字均小于Ki+1。 n為結點中關鍵字的個數,滿足 ceil(m/2)-1≤n≤m-1

三、B樹的插入

抽象情況分析:

1)根為空,創建新根,插入

2)根不為空,查找key,

若存在,不插入;

若不存在,插入到對應的葉子節點,若沒有滿,插入結束;若滿了就分裂出兄弟節點,分一半key和孩子給兄弟節點,提取key中位數,轉化成向父節點插入key中位數和兄弟節點,直到父節點滿足條件:沒有滿 或者 父節點的父節點為空,產生新的根節點。

具體情況分析:

以{53,139,75,49,145,36,50,47,101}為例:

插入53,139,75:

插入49,145,36:

插入50,47,101:

實現插入時特別注意:

1)分裂出兄弟節點時,拷貝一半key和一半孩子,最后剩一個右孩子拷貝;拷貝的孩子若不為空,要鏈接父節點指針。

2)兄弟節點的_n和原始節點的_n都要修正

3)兄弟節點的父指針要指向cur->parent

4)若cur的父節點為空,造新根,新根左為cur,右為brother,注意鏈接cur和brother的父指針。

5)若cur的父節點不為空,cur迭代到cur->parent,轉化成向cur->parent插入midKey和brother。

循環直到 當前節點未滿造出新根(4)。

insertKey細節:用index迭代從頭遍歷,直到找到比index大的位置,index及以后位置都往后挪動一位,同時挪動右孩子。若index一直走到尾,說明key是最大值,在尾賦值。最后統一鏈接右孩子,_n++。

代碼實現如下:

void insertKey(Node* cur, Node* sub, const K& key)
{size_t index = 0;while (index < cur->_n){//key大于當前位置,往后走if (key > cur->_keys[index]){++index;}else{//把index及以后key往后挪動一位//右孩子都向右挪動一位for (int i = cur->_n - 1; i >= (int)index; --i){cur->_keys[i + 1] = cur->_keys[i];cur->_subs[i + 2] = cur->_subs[i + 1];}cur->_keys[index] = key;break;}}if (index == cur->_n){cur->_keys[index] = key;}//鏈接右孩子cur->_subs[index + 1] = sub;cur->_n++;
}bool insert(const K& key)
{if (_root == nullptr){_root = new Node;_root->_keys[0] = key;_root->_n++;return true;}pair<Node*, int> ret = find(key);//不允許冗余if (ret.second != -1){return false;}//cur為要插入的葉子結點Node* cur = ret.first;K newkey = key;Node* brother = nullptr;while (true){insertKey(cur, brother, newkey);//未滿,結束if (cur->_n < M){break;}//滿了,分裂出兄弟節點brother = new Node;size_t mid = M / 2;size_t k = 0;//左key1 左孩子1 ... 左keyM 左孩子M 右孩子for (size_t i = mid + 1; i < M; ++i){//分一半keybrother->_keys[k] = cur->_keys[i];cur->_keys[i] = K();//分左孩子brother->_subs[k] = cur->_subs[i];//鏈接父指針if (brother->_subs[k] != nullptr){brother->_subs[k]->_parent = brother;}cur->_subs[i] = nullptr;++k;}//最后一個右孩子brother->_subs[k] = cur->_subs[M];//鏈接父指針if (brother->_subs[k] != nullptr){brother->_subs[k]->_parent = brother;}cur->_subs[M] = nullptr;//修正borther的_n和cur的_n(cur->_n還要提一個中位數給parent)brother->_n += M - mid - 1;cur->_n -= M - mid;//brother的parent指向cur->_parentbrother->_parent = cur->_parent;newkey = cur->_keys[mid];cur->_keys[mid] = K();//轉換成向parent插入一個midkey和brotherif (cur->_parent == nullptr){_root = new Node;_root->_keys[0] = newkey;_root->_subs[0] = cur;_root->_subs[1] = brother;cur->_parent = _root;brother->_parent = _root;_root->_n++;break;}else{cur = cur->_parent;}}return true;
}

?B樹的查找:

分析:每個節點的key都是有序序列,且是一維數組,支持隨機訪問,因此選用二分查找。

問題就是如果二分查找當前序列找不到怎么辦?

二分查找如果找不到,找出來的數據是可能比key大,也可能比key小的。但由于二分查找定位的特性,找出來的數據一定是keys數組中和與key最接近的二者之一,可能在key左邊,也可能在右邊,判斷一下就行了,比key小就在key的右孩子里找,反之在key的左孩子里找。

注:由于查找在插入時也要用,要用parent記錄cur的parent,以便于走到空時能記錄下葉子節點的指針。

代碼試下如下:

pair<Node*, int> find(const K& key)
{Node* cur = _root;Node* parent = nullptr;int begin, end, mid;while (cur){begin = 0;end = cur->_n - 1;//二分查找keywhile (begin <= end){mid = begin + ((end - begin) >> 1);// mid keyif (cur->_keys[mid] < key){begin = mid + 1;}//key midelse if (cur->_keys[mid] > key){end = mid - 1;}else{return { cur,mid };}}parent = cur;//沒有找到,mid是有效位置//可能大于或者小于keyif (cur->_keys[mid] < key){cur = cur->_subs[mid + 1];}else{cur = cur->_subs[mid];}}//cur走到nullptr,沒找到,返回葉子節點的指針return { parent,-1 };
}

B樹的中序遍歷:

分析:采用一個左孩子,一個關鍵字的方式遍歷,最后遍歷剩余的一個右孩子。

代碼如下:

void inorder()
{_inorder(_root);cout << endl;
}
void _inorder(Node* root)
{if (root == nullptr)return;//左1 根1 ... 左_n 根_n 右for (size_t i = 0; i < root->_n; ++i){_inorder(root->_subs[i]);cout << root->_keys[i] << " ";}_inorder(root->_subs[root->_n]);
}

B樹性能分析:

B- 樹的效率是很高的,對于 N = 62*1000000000 個節點,如果度 M 1024,則logM/2(?N)≤4 ,即在620 億個元素中,如果這棵樹的度為 1024 ,則需要小于 4 次即可定位到該節點,然后利用 二分查找可以快速定位到該元素,大大減少了讀取磁盤的次數
對于一顆滿的B樹, 查找的時間復雜度為O(logM(N))。

四、B+樹和B*

B+樹:

B+ 樹是 B 樹的變形,是在 B 樹基礎上優化的多路平衡搜索樹, B+ 樹的規則跟 B 樹基本類似,但是又
B 樹的基礎上做了以下幾點改進優化:
1)分支節點的子樹指針與關鍵字個數相同
2)分支節點的子樹指針 p[i] 指向關鍵字值大小在 [k[i] k[i+1]) 區間之間
3)所有葉子節點增加一個鏈接指針鏈接在一起(核心)
4)所有關鍵字及其映射數據都在葉子節點出現(核心)
分析:1)和 2)優化了B樹操作的便捷性,等同于刪除了第一個節點的左孩子,且2)的優化存節點的最小值,進一步提高了查找的效率。
3)和 4)優化了B樹的結構,將所有關鍵字及其映射數據都在葉子結點出現,并且葉子節點之間相連,方便遍歷。
B+ 樹的特性:
1. 所有關鍵字都出現在葉子節點的鏈表中,且鏈表中的節點都是有序的。
2. 不可能在分支節點中命中。
3. 分支節點相當于是葉子節點的索引,葉子節點才是存儲數據的數據層。

B*樹:

B* 樹是 B+ 樹的變形,在 B+ 樹的非根和非葉子節點再增加指向兄弟節點的指針。
B* 樹的分裂:
當一個結點滿時,如果它的下一個兄弟結點未滿,那么將一部分數據移到兄弟結點中,再在原結點插入關鍵字,最后修改父結點中兄弟結點的關鍵字(因為兄弟結點的關鍵字范圍改變了);如果兄弟也滿了,則在原結點與兄弟結點之間增加新結點,并各復制1/3 的數據到新結點,最后在父結點增加新結點的指針。
總結:當一個節點滿時,轉移到下一個兄弟,直到所有的節點都滿了,分裂新的節點。
B* 樹分配新結點的概率比 B+ 樹要低,空間使用率更高。
總結
B 樹:有序數組 + 平衡多叉樹;
B+ 樹:有序數組鏈表 + 平衡多叉樹;
B* 樹:一棵更豐滿的,空間利用率更高的 B+ 樹。

在內存中做內查找:?

B樹系列與哈希和平衡二叉搜索樹對比:

單純論樹高度,搜索效率而言,B樹快。

B樹系列隱形壞處:

1)空間利用率低,消耗高

2)插入刪除數據時,分裂和合并節點,那么必然挪動數據

3)在內存中而言,跟哈希和平衡二叉搜索樹還是一個量級。

結論:實質上B樹系列在內存中體現不出優勢。

五、B-樹的應用

1.索引
B- 樹最常見的應用就是用來做索引
MySQL 官方對索引的定義為: 索引 (index) 是幫助 MySQL 高效獲取數據的數據結構,簡單來說: 索引就是數據結構
2.MySQL 索引簡介
mysql 是目前 非常流行的開源關系型數據庫,不僅是免費的,可靠性高,速度也比較快,而且擁 有靈活的插件式存儲引擎。
MySQL 中索引屬于存儲引擎級別的概念,不同存儲引擎對索引的實現方式是不同的。
意:索引是基于表的,而不是基于數據庫的。
3.MyISAM
MyISAM 引擎是 MySQL5.5.8 版本之前默認的存儲引擎,不支持事物支持全文檢索,使用 B+Tree作為索引結構,葉節點的data 域存放的是數據記錄的地址,其結構如下:
MyISAM 的索引文件僅僅保存數據記錄的 地址 MyISAM 中,主索引和輔助索引( Secondary key )在結構上沒有任何區別,只是主索 引要求 key 是唯一的,而輔助索引的 key 可以重復。
Secondary key同樣也是一棵B+Tree data 域保存數據記錄的地址。因此, MyISAM 中索引檢索的算法為首先按照B+Tree 搜索算法搜索索引,如果指定的 Key 存在,則取出其 data 域的值,然后以 data 域的值為地址,讀取相應數據記錄。MyISAM 的索引方式也叫做 非聚集索引 的。
分支節點只存儲key,比較小。
分支節點映射的磁盤塊就可以盡可能加載Cache。
對于SQL分析:
update stuInfo?set age = 35 where stuID = 50
update stuInfo?set age = 35 where name?= 'Rose'
這兩句SQL語句的性能不同。
第一種,stuID為主鍵,主索引,結構是一顆B+樹,查找效率為O(logM(N))
第二種,name不是主鍵,且沒有索引,查找時暴力遍歷,效率O(N)
若想經常用name進行查找, 給name創建普通索引,相當于使用name做key創建一顆B+樹,value指向磁盤數據,這時候查找效率就高了。
B+樹做主鍵索引比起B樹的優勢:
1)B+樹所有值都在葉子,遍歷很方便,方便區間查找。
2)對于沒有建立索引的字段,全表掃描很方便。
3)分值節點存儲key,一個分支節點占用空間更少,可以盡可能加載到緩存。
B樹優勢:不用帶葉子就能查找到值,但B+樹高度不高,效率差別不大。
4.InnoDB:
InnoDB 存儲引擎支持事務 ,其設計目標主要面向在線事務處理的應用,從 MySQL 數據庫 5.5.8 本開始, InnoDB 存儲引擎是默認的存儲引擎 InnoDB 支持 B+ 樹索引、全文索引、哈希索引。但InnoDB使用 B+Tree 作為索引結構時,具體實現方式卻與 MyISAM 截然不同。
第一個區別是 InnoDB 的數據文件本身就是索引文件 MyISAM 索引文件和數據文件是分離的, 索引文件僅保存數據記錄的地址
最大區別:每個葉子節點數據都是以文件形式存到磁盤上,數據和主鍵索引放在一起。 葉節點包含了完整的數據記錄, 這種索引叫做聚集索引
所以 InnoDB 要求表必須有主鍵 MyISAM 可以沒有), 如果沒有顯式指定,則 MySQL 系統會自動選擇一個可以唯一標識數據記錄的列作為主鍵 如果不存在這種列,則 MySQL 自動為 InnoDB 表生成一個隱含字段作為主鍵,這個字段長度為6 個字節,類型為長整型。
第二個區別是 InnoDB 的? 輔助索引data域存儲相應記錄主鍵的值而不是地址? , 所有輔助索引都引用主鍵作為data 域。
聚集索引這種實現方式使得按主鍵的搜索十分高效 ,但是 輔助索引搜索需要檢索兩遍索引:首先 檢索輔助索引獲得主鍵,然后用主鍵到主索引中檢索獲得記錄。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/75832.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/75832.shtml
英文地址,請注明出處:http://en.pswp.cn/web/75832.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CTF--shell

一、原題 &#xff08;1&#xff09;提示&#xff1a; $poc"a#s#s#e#r#t";$poc_1explode("#",$poc);$poc_2$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; $poc_2($_GET[s]) &#xff08;2&#xff09;原網頁&#xff1a;一片空白什么都…

基于51單片機的正負5V數字電壓表( proteus仿真+程序+設計報告+講解視頻)

基于51單片機的正負5V數字電壓表( proteus仿真程序設計報告講解視頻&#xff09; 仿真圖proteus7.8及以上 程序編譯器&#xff1a;keil 4/keil 5 編程語言&#xff1a;C語言 設計編號&#xff1a;S0101 1. 主要功能&#xff1a; 設計一個基于51單片機數字電壓表 1、能夠…

hive數倉要點總結

1.OLTP和OLAP區別 OLTP&#xff08;On-Line Transaction Processing&#xff09;即聯機事務處理&#xff0c;也稱為面向交易的處理過程&#xff0c;其基本特征是前臺接收的用戶數據可以立即傳送到計算中心進行處理&#xff0c;并在很短的時間內給出處理結果&#xff0c;是對用…

【實戰手冊】8000w數據遷移實踐:MySQL到MongoDB的完整解決方案

?? 本文將帶你深入解析大規模數據遷移的實踐方案,從架構設計到代碼實現,手把手教你解決數據遷移過程中的各種挑戰。 ??博主其他匠心之作,強推專欄: 小游戲開發【博主強推 匠心之作 拿來即用無門檻】文章目錄 一、場景引入1. 問題背景2. 場景分析為什么需要消息隊列?為…

運行小程序需要選擇什么配置的服務器

主要是看有多少人瀏覽&#xff0c;如果是每天有幾十個人瀏覽&#xff0c;通常2核或者4核就可以滿足需求&#xff0c;內存的話建議4g或者8g&#xff0c;足夠的內存可以使服務器同時處理多個請求&#xff0c;避免因內存不足導致的卡頓或程序崩潰。 硬盤存儲方面&#xff0c;50GB…

基于SpringBoo的地方美食分享網站

作者&#xff1a;計算機學姐 開發技術&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源碼”。 專欄推薦&#xff1a;前后端分離項目源碼、SpringBoot項目源碼、Vue項目源碼、SSM項目源碼、微信小程序源碼 精品專欄&#xff1a;…

Solidity私有函數和私有變量區別,私有變量可以被訪問嗎

web3面試題 私有函數和私有變量區別&#xff0c;私有變量可以被訪問嗎 ChatGPT said: 在 Web3 開發&#xff0c;尤其是使用 Solidity 編寫智能合約時&#xff0c;關于私有函數和私有變量的區別是常見的面試題。下面是詳細解析&#xff1a; ? 私有函數&#xff08;Private Fu…

mongodb 安裝配置

1.下載 官網下載地址&#xff1a;MongoDB Community Download | MongoDB 2.使用解壓包 解壓包安裝&#xff1a;https://pan.baidu.com/s/1Er56twK9UfxoExuCPlJjhg 提取碼: 26aj 3.配置環境&#xff1a; &#xff08;1&#xff09;mongodb安裝包位置&#xff1a; &#xf…

多模態大語言模型arxiv論文略讀(十九)

MLLMs-Augmented Visual-Language Representation Learning ?? 論文標題&#xff1a;MLLMs-Augmented Visual-Language Representation Learning ?? 論文作者&#xff1a;Yanqing Liu, Kai Wang, Wenqi Shao, Ping Luo, Yu Qiao, Mike Zheng Shou, Kaipeng Zhang, Yang Yo…

[LeetCode 45] 跳躍游戲2 (Ⅱ)

題面&#xff1a; LeetCode 45 跳躍游戲2 數據范圍&#xff1a; 1 ≤ n u m s . l e n g t h ≤ 1 0 4 1 \le nums.length \le 10^4 1≤nums.length≤104 0 ≤ n u m s [ i ] ≤ 1000 0 \le nums[i] \le 1000 0≤nums[i]≤1000 題目保證可以到達 n u m s [ n ? 1 ] nums[…

前端面試寶典---閉包

閉包介紹 使用閉包&#xff1a; 在函數內聲明一個變量&#xff0c;避免外部訪問在該函數內再聲明一個函數訪問上述變量&#xff08;閉包&#xff09;返回函數內部的函數使用完畢建議閉包函數null;譯放內存 function createCounter() {let count 0;return function () {coun…

GPT4O畫圖玩法案例,不降智,非dalle

網址如下&#xff1a; 玩法1&#xff1a;吉卜力&#xff08;最火爆&#xff09; 提示詞&#xff1a;請將附件圖片轉化為「吉卜力」風格&#xff0c;尺寸不變 玩法2&#xff1a;真人繪制 提示詞&#xff1a;創作一張圖片&#xff0c;比例4:3&#xff0c;一個20歲的中國女孩…

4.12~4.14【Q】cv homework6

我正在寫GAMES101作業6&#xff0c;在這段代碼中&#xff0c;我十分想知道inline Intersection Triangle::getIntersection(Ray ray) 是由哪個函數&#xff0c;哪段代碼調用的&#xff1f;什么是Inline&#xff1f;詳細解釋&#xff0c;越細節越好 我正在寫GAMES101作業6&…

MATLAB雙目標定

前言&#xff1a; 現在有許多雙目攝像頭在出廠時以及標定好&#xff0c;用戶拿到手后可以直接使用&#xff0c;但也有些雙目攝像頭在出廠時并沒有標定。因而這個時候就需要自己進行標定。本文主要介紹基于matlab工具箱的自動標定方式來對雙目相機進行標定。 1、MATLAB工具箱標…

visual studio 常用的快捷鍵(已經熟悉的就不記錄了)

以下是 Visual Studio 中最常用的快捷鍵分類整理&#xff0c;涵蓋代碼編輯、調試、導航等核心場景&#xff1a; 一、生成與編譯 ?生成解決方案 Ctrl Shift B 一鍵編譯整個解決方案&#xff0c;檢查編譯錯誤&#xff08;最核心的生成操作&#xff09;?編譯當前文件 Ctrl F…

Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.

今天寫項目的時候碰到一個報錯&#xff0c;在網上查找到了解決方法&#xff0c;這里備份一下。防止下次再次遇到 原文章鏈接&#xff1a;Sass import rules are deprecated and will be removed in Dart Sass 3.0.0. 報錯內容如下&#xff1a; Deprecation Warning: Sass i…

【QT】QWidget 概述與核心屬性(API)

&#x1f308; 個人主頁&#xff1a;Zfox_ &#x1f525; 系列專欄&#xff1a;Qt 目錄 一&#xff1a;&#x1f525; 控件概述 &#x1f98b; 控件體系的發展階段 二&#xff1a;&#x1f525; QWidget 核心屬性 &#x1f98b; 核心屬性概覽&#x1f98b; 用件可用&#xff08…

Redis 在處理并發請求時,如何保證高效性和數據一致性

1. 單線程模型&#xff08;核心命令處理&#xff09; 單線程優勢&#xff1a;Redis 的核心命令處理是單線程的&#xff08;基于內存操作&#xff0c;避免多線程競爭&#xff09;&#xff0c;所有命令按順序執行&#xff0c;天然避免了多線程的鎖競爭和上下文切換開銷。非阻塞 …

flutter-Text等組件出現雙層黃色下劃線的問題

文章目錄 1. 現象2. 原因3. 解決方法 1. 現象 這天我正在寫Flutter項目的頁面功能&#xff0c;突然發現我的 Text 文字出現了奇怪的樣式&#xff0c;具體如下&#xff1a; 文字下面出現了雙層黃色下劃線文字的空格變得很大&#xff0c;文字的間距也變得很大 我百思不得其解&a…

cursor+高德MCP:制作一份旅游攻略

高德開放平臺 | 高德地圖API (amap.com) 1.注冊成為開發者 2.進入控制臺選擇應用管理----->我的應用 3.新建應用 4.點擊添加Key 5.在高德開發平臺找到MCP的文檔 6.按照快速接入的步驟&#xff0c;進行操作 一定要按照最新版的cursor, 如果之前已經安裝舊的版本卸載掉重新安…