Java中的TreeMap

TreeMap繼承自AbstractMap,并實現了NavigableMap接口(NavigableMap繼承自SortedMap接口)。底層的數據結構是紅黑樹,按照鍵的自然排序或者自定義實現的規則排序,實現元素的有序性。

特點

  1. 元素是有序的:按照key的自然排序或者是自定義的比較器進行排序(并不是按照插入順序排序)
  2. 鍵不能重復,值可以重復:key不能重復,value可以重復
  3. 鍵不能為null,值可以為null
  4. 紅黑樹是一種自平衡的二叉樹,通過規則維持樹的高度近似平衡,保證查詢的時間復雜度為O(log n)

紅黑樹底層原理

  • TreeMap是基于紅黑樹實現的鍵值對集合
  • 樹的根節點為黑色
  • 樹里面的節點不是黑色的就是紅色的
  • 相鄰的兩個節點不能都是紅色
  • 從任意一個節點到其葉子節點的路徑上黑色節點數相同
  • 紅黑樹的所有葉子節點都是黑色(葉子節點是Nil節點)

參考博主:

紅黑樹插入: https://www.cnblogs.com/GNLin0820/p/9120337.html最終還是決定把紅黑樹的篇章一分為二,插入操作一篇,刪除操作一篇,因為合在一起寫篇幅實在太長了,寫起來都覺得累,何況是閱讀并理解的讀者。 紅黑樹刪除操作請參考?數據結構 - 紅黑樹(Red Black Tree)刪除詳解與實現(Java) 現在網絡上最不缺的就是對某個知識點的講解博文,各種花https://www.cnblogs.com/GNLin0820/p/9120337.html

紅黑樹刪除: https://www.cnblogs.com/GNLin0820/p/9668378.html本篇要講的就是紅黑樹的刪除操作 紅黑樹插入操作請參考?數據結構 - 紅黑樹(Red Black Tree)插入詳解與實現(Java) 紅黑樹的刪除是紅黑樹操作中比較麻煩且比較有意思的一部分。 在此之前,重申一遍紅黑樹的五個定義: 1. 紅黑樹的節點不是黑色的就是紅色的 2. 紅黑樹的根節點https://www.cnblogs.com/GNLin0820/p/9668378.html

添加節點

第一種情況

添加的節點(500)和它的父節點(400)也是紅色,并且它的叔叔節點(Nil1)是黑色(Nil是不存在的葉子節點)

添加的節點(500)是叔叔節點(Nil1)的遠親(是父節點的右節點)

就以父節點(400)為中心點,左旋。旋轉后:

  1. 父節點變成黑色,祖父節點變成紅色
  2. 祖父節點(300)的父節點變成400,祖父節點(300)的左子節點是Nil1,右子節點是Nil2
  3. 父節點(400)的左子節點變成了300,右子節點是500
  4. 祖父節點(300)有父節點的話,父節點(400)的父節點 變成 祖父節點(300)的父節點

?第二種情況

添加的節點(350)是父節點(400)的左子節點,父節點是紅色;它的叔叔節點(Nil1)是黑色

添加的節點(350)是叔叔節點(Nil1)的近親(父節點的左子節點)

就以父節點(400)為中心,先右旋。旋轉后:

  1. 父節點(400)變成了插入節點(350)的右節點,400的父節點變成了350,400的左節點變成了Nil4
  2. 插入節點(350)的右節點變成了400,它的父節點變成了300

再左旋:以350作為中心點,左旋

第三種情況

添加節點(100)是父節點的左子節點,父節點(200)是紅色,它的叔叔節點(Nil4)是黑色

添加的節點(100)是叔叔節點的遠親(挨的遠)

則以父節點(200)為中心點,右旋,旋轉后:

  1. 父節點(200)變成黑色,祖父節點(300)變成紅色
  2. 300的父節點變成了200,左子節點變成了Nil3,右子節點變成了Nil4
  3. 200的右子節點變成了300,左子節點變成了100
  4. 如果祖父節點(300)不是根節點,父節點(200)的父節點 變成 祖父節點(300)的父節點

第四種情況

插入節點(250)是父節點的右子節點,父節點為祖父節點(300)的左子節點,父節點(200)是紅色

插入節點(250)的叔叔節點(Nil4)是黑色

250與Nil4是近親(挨的近)

以父節點(200)為中心點,先左旋:

  1. 200的父節點變成250,200的左子節點是Nil1,右子節點是Nil2
  2. 250的父節點變成300,250的左子節點變成200

再按照第三種情況,以250為中心點右旋

第五種情況

插入節點為紅色,它的父節點也為紅色,它的叔叔節點也是紅色

直接將父節點和叔叔節點變成黑色,祖父節點變成紅色

如果祖父節點是根節點,則祖父節點最后要變成黑色

  1. 首先確認插入節點位置,定位好插入節點的父節點,祖父節點,叔叔節點

  2. 看自己和父節點顏色,都是紅色就需要修復樹平衡(相鄰節點不能都是紅色)

  3. 確認要修復平衡后,看叔叔節點顏色

    1. 叔叔是紅色,直接改變叔叔和父親的顏色(變成黑色),再改變祖父節點顏色(變為紅色)

    2. 叔叔是黑色,就要左旋或者右旋

      1. 自己是左節點,父親也是左節點:自己是紅色,父節點變成黑色,祖父節點變成紅色。以父親為中心右旋

      2. 自己是左節點,父親是右節點:以父親為中心,先右旋再左旋,旋轉完之后變換顏色,自己變成黑色,父親節點和祖父節點變成紅色

      3. 自己是右節點,父親也是右節點:自己是紅色,父節點變成黑色,祖父節點變成紅色。以父親為中心左旋

      4. 自己是右節點,父親是左節點:以父親為中心,先左旋再右旋,旋轉完之后變換顏色,自己變成黑色,父親節點和祖父節點變成紅色

    3. 完成當次平衡后,如果叔叔是紅色節點:把祖父節點作為下一次平衡的插入點;如果叔叔是黑色節點:把父親節點作為下一次平衡的插入點

    4. 直到遍歷到根節點或者是當前插入點的父節點是黑色,結束平衡操作

刪除節點(部分)

首先要確認刪除節點時不存在的情況

第二,四種:不滿足條件:相鄰的兩個節點不能為紅色

第一,三,五,六種:不滿足條件:從任意節點到葉子節點路徑上的黑色節點數要相同

也就是說:

在第一五種情況下:父節點是紅色,只有一個子節點且是黑色,這時候必然會有另一個子節點也是黑色

在第二六種情況下:父節點是黑色,只有一個子節點且是黑色,這時候必然會有另一個子節點也是黑色

以下是會發生的刪除情況

第一種情況

被刪除的節點是黑色,并且只有一個子節點是紅色

  1. 100這個節點作為修復樹的起點(代碼中設置為replacement)
  2. 100的父節點改為400
  3. 200的所有指針都置為空
  4. 100作為平衡樹的起點去做平衡修復:100的顏色是紅色,沒有其他操作,直接將100這個節點的顏色變為黑色

第二種情況

要刪除的節點p有左右子節點

  1. 找到500的后繼節點:就是600,被確認為要刪除的節點
  2. 將600節點里面的key-value復制到500節點
  3. p指針變成指向600節點
  4. 600節點沒有子節點,所以沒有replacement作為作為修復平衡樹的起點
  5. 600節點有父節點:600節點自己作為修復平衡樹的起點
  6. 600節點是紅色,不需要修復平衡,直接將600節點中的指針清空

第三種情況

要刪除的節點為450,有左右節點。

  1. 確認其后繼節點為600,將600節點的key-value復制給450節點,600被確認為要刪除的節點
  2. 600(p指針指向的節點,以下皆為p指向)沒有子節點,則600作為修復樹的起點
  3. 600節點的顏色為黑色(被刪除的節點是黑色,破壞了樹的平衡),修復樹
  4. 600的兄弟節點只有一個子節點,并且和600是近親,420和440節點顏色互換
  5. 以420為中心左旋
  6. 以440為中心右旋
  7. 清空p指針指向的節點

第四種情況

方法

構造方法

public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{//比較器private final Comparator<? super K> comparator;//根節點private transient Entry<K,V> root;//樹中節點個數private transient int size = 0;//樹的修改次數private transient int modCount = 0;public TreeMap() {comparator = null;}//創建一個TreeMap,自定義比較器public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}//創建一個TreeMap,將傳入集合中的元素復制到TreeMap中public TreeMap(Map<? extends K, ? extends V> m) {comparator = null;putAll(m);}//創建一個TreeMap,將傳入的集合復制到TreeMap中,使用傳入集合中的比較器public TreeMap(SortedMap<K, ? extends V> m) {comparator = m.comparator();try {buildFromSorted(m.size(), m.entrySet().iterator(), null, null);} catch (java.io.IOException | ClassNotFoundException cannotHappen) {}}
}

查找當前節點的后繼節點

    //查找當前節點的后繼節點(比當前節點大的最小節點)static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {if (t == null)return null;//如果t節點的右節點不為空,則查找該右節點的最小左節點else if (t.right != null) {Entry<K,V> p = t.right;while (p.left != null)p = p.left;return p;} //如果t節點的右節點為空,則查找該節點的父節點,若t是父節點的左節點,則返回該父節點else {Entry<K,V> p = t.parent;Entry<K,V> ch = t;//若t是p(父節點)的右節點,則繼續向上查找,直到查找到p,其中t是p的左節點,返回pwhile (p != null && ch == p.right) {ch = p;p = p.parent;}return p;}}

刪除節點后自平衡

x節點是修復平衡的起點

    //x為刪除方法中計算出的后繼者節點的子節點或者是后繼者節點本身//如果x節點不是根節點且它的顏色是黑色,則需要左旋或者右旋//否則直接將x節點的顏色變為黑色private void fixAfterDeletion(Entry<K,V> x) {//如果x節點不是根節點且它的顏色是黑色while (x != root && colorOf(x) == BLACK) {//如果x節點是左節點if (x == leftOf(parentOf(x))) {Entry<K,V> sib = rightOf(parentOf(x));//同一個父節點,x作為左節點是黑色,sib作為右節點是紅色//將sib設置為黑色,父節點設置為紅色//以x的父節點為中心點左旋if (colorOf(sib) == RED) {setColor(sib, BLACK);setColor(parentOf(x), RED);rotateLeft(parentOf(x));sib = rightOf(parentOf(x));}//sib的左節點是黑色,sib的右節點也是黑色,設置sib為紅色//設置完成后將x的父節點賦值給xif (colorOf(leftOf(sib))  == BLACK &&colorOf(rightOf(sib)) == BLACK) {setColor(sib, RED);x = parentOf(x);} //如果sib的左右節點其中一個是紅色else {//如果sib的右節點是黑色,設置sib的左節點為黑色,sib為紅色,以sib為中心右旋//將x節點的父節點的右節點賦值給sibif (colorOf(rightOf(sib)) == BLACK) {setColor(leftOf(sib), BLACK);setColor(sib, RED);rotateRight(sib);sib = rightOf(parentOf(x));}//上面if條件執行與否都會執行下面的代碼//將sib的顏色設置為x父節點的顏色//將x父節點的顏色設置為黑色//將sib的右節點顏色設置為黑色//以x的父節點為中心左旋setColor(sib, colorOf(parentOf(x)));setColor(parentOf(x), BLACK);setColor(rightOf(sib), BLACK);rotateLeft(parentOf(x));//將x設置為根節點,跳出循環x = root;}} //如果x是右節點,與是左節點的情況是對稱的else {Entry<K,V> sib = leftOf(parentOf(x));if (colorOf(sib) == RED) {setColor(sib, BLACK);setColor(parentOf(x), RED);rotateRight(parentOf(x));sib = leftOf(parentOf(x));}if (colorOf(rightOf(sib)) == BLACK &&colorOf(leftOf(sib)) == BLACK) {setColor(sib, RED);x = parentOf(x);} else {if (colorOf(leftOf(sib)) == BLACK) {setColor(rightOf(sib), BLACK);setColor(sib, RED);rotateLeft(sib);sib = leftOf(parentOf(x));}setColor(sib, colorOf(parentOf(x)));setColor(parentOf(x), BLACK);setColor(leftOf(sib), BLACK);rotateRight(parentOf(x));x = root;}}}setColor(x, BLACK);}

添加單個元素

   public V put(K key, V value) {Entry<K,V> t = root;if (t == null) {compare(key, key); // type (and possibly null) checkroot = new Entry<>(key, value, null);size = 1;modCount++;return null;}int cmp;Entry<K,V> parent;Comparator<? super K> cpr = comparator;//比較器不為空,根據比較器規則查找元素,若key已存在,更新key對應的value值if (cpr != null) {do {parent = t;cmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}//比較器為空,則按照自然順序排序比較,查找key,若key存在,則更新key對應的value值else {if (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key;do {parent = t;cmp = k.compareTo(t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}//key不存在,新建一個節點存放鍵值對,放入紅黑樹中Entry<K,V> e = new Entry<>(key, value, parent);if (cmp < 0)parent.left = e;elseparent.right = e;//添加完成后,調用函數保證紅黑樹的自平衡fixAfterInsertion(e);size++;modCount++;return null;}

添加多個元素

    //按照map的比較器添加元素或者是按照默認自然排序規則添加元素public void putAll(Map<? extends K, ? extends V> map) {int mapSize = map.size();if (size==0 && mapSize!=0 && map instanceof SortedMap) {Comparator<?> c = ((SortedMap<?,?>)map).comparator();if (c == comparator || (c != null && c.equals(comparator))) {++modCount;try {buildFromSorted(mapSize, map.entrySet().iterator(),null, null);} catch (java.io.IOException | ClassNotFoundException cannotHappen) {}return;}}super.putAll(map);}

刪除單個元素

   private void deleteEntry(Entry<K,V> p) {modCount++;size--;//如果要刪除的節點,左右節點都不為空,查找到當前節點的后繼節點//將后繼節點的鍵值對數據拷貝到要刪除的節點中,并且將后繼者節點作為被刪除的節點if (p.left != null && p.right != null) {Entry<K,V> s = successor(p);p.key = s.key;p.value = s.value;//將后繼者節點賦值給p,后續操作的p其實是此處查找到的后繼者節點p = s;} // p has 2 children//指定從replacement開始修復樹的平衡//從后繼者節點的左節點或者右節點開始Entry<K,V> replacement = (p.left != null ? p.left : p.right);//如果replacement節點不為空,后繼者節點的父節點與成為replacement節點的父節點if (replacement != null) {replacement.parent = p.parent;//如果后繼者節點的父節點為空,則replacement作為根節點if (p.parent == null)root = replacement;//如果后繼者節點是其父節點的左節點,則replacement成為父節點的左節點else if (p == p.parent.left)p.parent.left  = replacement;//如果后繼者節點是其父節點的右節點,則replacement成為父節點的右節點elsep.parent.right = replacement;//將后繼者節點的指針都清空p.left = p.right = p.parent = null;// 修復樹的平衡,從replacement節點開始//如果刪除的后繼者節點是黑色,調用修復if (p.color == BLACK)fixAfterDeletion(replacement);} //如果后繼者節點沒有父節點,并且也沒有子節點(左右節點),那么清空根節點else if (p.parent == null) {root = null;} //如果后繼者節點沒有子節點但是有父節點,那么后繼者節點自己作為樹平衡的開始節點else { //修復樹平衡if (p.color == BLACK)fixAfterDeletion(p);//刪除后繼者節點(清除后繼者節點的指針,以及后繼者節點的父節點指向它的指針)if (p.parent != null) {if (p == p.parent.left)p.parent.left = null;else if (p == p.parent.right)p.parent.right = null;p.parent = null;}}}

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

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

相關文章

vue3表單驗證的時候訪問接口如果有值就通過否則不通過.主動去觸發校驗

頁面有個身份證號碼的校驗。校驗完身份證格式是否符合之后還要去訪問接口查詢這個用戶是否存在。如果存在才通過驗證。否則就校驗不通過 <el-form ref"ruleFormRef" :model"form" label-width"140px" label-position"right" label…

Python常見面試題的詳解24

1. 如何對關鍵詞觸發模塊進行測試 要點 功能測試&#xff1a;驗證正常關鍵詞觸發、邊界情況及大小寫敏感性&#xff0c;確保模塊按預期響應不同輸入。 性能測試&#xff1a;關注響應時間和并發處理能力&#xff0c;保證模塊在不同負載下的性能表現。 兼容性測試&#xff1a;測…

前端Javascrip后端Net6前后分離文件上傳案例(完整源代碼)下載

文件上傳功能在項目開發中非常實用&#xff0c;本案例前端用Javascrip實現&#xff0c;后端用Net6實現 前端Javascrip后端Net6前后分離文件上傳案例&#xff08;完整源代碼&#xff09; 下載鏈接 https://download.csdn.net/download/luckyext/90437795?spm1001.2014.3001.5…

DeepSeek行業應用實踐報告-智靈動力【112頁PPT全】

DeepSeek&#xff08;深度搜索&#xff09;近期引發廣泛關注并成為眾多企業/開發者爭相接入的現象&#xff0c;主要源于其在技術突破、市場需求適配性及生態建設等方面的綜合優勢。以下是關鍵原因分析&#xff1a; 一、技術核心優勢 開源與低成本 DeepSeek基于開源架構&#xf…

C語言綜合案例:學生成績管理系統

C語言綜合案例&#xff1a;學生成績管理系統 需求 1.存儲最多50名學生的信息&#xff08;不使用結構體&#xff09; 2.每個學生包含&#xff1a; 學號&#xff08;字符數組&#xff09;姓名&#xff08;字符數組&#xff09;3門課程成績&#xff08;一維數組&#xff09; …

Day 51 卡瑪筆記

這是基于代碼隨想錄的每日打卡 647. 回文子串 給你一個字符串 s &#xff0c;請你統計并返回這個字符串中 回文子串 的數目。 回文字符串 是正著讀和倒過來讀一樣的字符串。 子字符串 是字符串中的由連續字符組成的一個序列。 示例 1&#xff1a; 輸入&#xff1a;s &q…

結構型模式---外觀模式

概念 外觀模式是一種結構型設計模式&#xff0c;它的核心思想是為復雜的子系統提供一個統一的接口&#xff0c;簡化客戶端與子系統的交互。外觀模式通過引入一個高層接口&#xff0c;隱藏子系統的復雜性&#xff0c;使客戶端更容易使用。 適用場景 用于客戶端無需具體操作子…

DeepSeek開源周第二彈:DeepEP如何用RDMA+FP8讓MoE模型飛起來?

一、引言&#xff1a;MoE模型的通信瓶頸與DeepEP的誕生 在混合專家&#xff08;MoE&#xff09;模型訓練中&#xff0c;專家間的全對全&#xff08;All-to-All&#xff09;通信成為性能瓶頸。傳統方案在跨節點傳輸時帶寬利用率不足50%&#xff0c;延遲高達300μs以上。DeepSee…

多通道數據采集和信號生成的模塊化儀器如何重構飛機電子可靠性測試體系?

飛機的核心電子系統包括發電與配電系統&#xff0c;飛機內部所有設備和系統之間的內部數據通信系統&#xff0c;以及用于外部通信的射頻設備。其他所有航空電子元件都依賴這些關鍵總線進行電力傳輸或數據通信。在本文中&#xff0c;我們將了解模塊化儀器&#xff08;無論是PCIe…

【Godot4.3】基于繪圖函數的矢量蒙版效果與UV換算

概述 在設計圓角容器時突發奇想&#xff1a; 將圓角矩形的每個頂點坐標除以對應圓角矩形所在Rect2的size&#xff0c;就得到了頂點對應的UV坐標。然后使用draw_colored_polygon&#xff0c;便可以做到用圖片填充圓角矩形的效果。而且這種計算的效果就是圖片隨著其填充的圖像縮…

數據存儲:一文掌握存儲數據到MongoDB詳解

文章目錄 一、環境準備1.1 安裝MongoDB1.2 安裝Python MongoDB驅動 二、連接到MongoDB2.1 基本連接2.2 連接到MongoDB Atlas&#xff08;云服務&#xff09; 三、基本CRUD操作3.1 創建&#xff08;Create&#xff09;&#xff1a;插入數據3.2 讀取&#xff08;Read&#xff09;…

算法教程:島的最大面積

算法教程:島的最大面積 我們將首先討論問題和解決方案,然后使用可視化工具(上一篇博客中進行了介紹)來更好地理解搜索過程。 問題描述 我們將要演練的具體問題是問題 Leetcode:島嶼的最大面積。在 Leetcode 上找到的直接問題描述是: 給你一個 m x n 二進制矩陣網格。島…

Scrapy:隧道代理中移除 Proxy-Authorization 的原理解析

隧道代理中移除 Proxy-Authorization 的原理解析 背景 在 Scrapy 的 HTTP 下載處理中&#xff0c;當使用隧道代理&#xff08;TunnelingAgent&#xff09;時&#xff0c;會移除請求頭中的 Proxy-Authorization。這個操作看似簡單&#xff0c;但背后有著重要的安全考慮和技術原…

大中型虛擬化園區網絡設計

《大中型虛擬化園區網絡設計》屬于博主的“園區網”專欄&#xff0c;若想成為HCIE&#xff0c;對于園區網相關的知識需要非常了解&#xff0c;更多關于園區網的內容博主會更新在“園區網”專欄里&#xff0c;請持續關注&#xff01; 一.前言 華為云園區網絡解決方案(簡稱Cloud…

sklearn中的決策樹-分類樹:剪枝參數

剪枝參數 在不加限制的情況下&#xff0c;一棵決策樹會生長到衡量不純度的指標最優&#xff0c;或者沒有更多的特征可用為止。這樣的決策樹 往往會過擬合。為了讓決策樹有更好的泛化性&#xff0c;我們要對決策樹進行剪枝。剪枝策略對決策樹的影響巨大&#xff0c;正確的剪枝策…

幾個api

幾個api 原型鏈 可以閱讀此文 Function instanceof Object // true Object instanceof Function // true Object.prototype.isPrototypeOf(Function) // true Function.prototype.isPrototypeOf(Object) // true Object.__proto__ Function.prototype // true Function.pro…

【Azure 架構師學習筆記】- Azure Databricks (12) -- Medallion Architecture簡介

本文屬于【Azure 架構師學習筆記】系列。 本文屬于【Azure Databricks】系列。 接上文 【Azure 架構師學習筆記】- Azure Databricks (11) – UC搭建 前言 使用ADB 或者數據湖&#xff0c;基本上繞不開一個架構“Medallion”&#xff0c; 它使得數據管理更為簡單有效。ADB 通過…

Android手機部署DeepSeek

1.概述 android手機端部署deepseek一般需要安裝termux,ollama,deepseek三個大的步驟 原因分析&#xff1a;deepseek等大模型需要類似ollama的工具去運行。ollama有mac window和linux版本&#xff0c;無Android版本&#xff1b;termux是一個模擬linux環境的Android app&#x…

計算機科學技術領域的內卷現狀與應對措施分析

計算機科學技術領域的內卷現狀與應對措施分析 李升偉 整理 ### 計算機科學技術領域的內卷現狀與應對措施分析 #### 一、內卷現狀分析 1. **教育與升學內卷** 計算機科學與技術相關專業&#xff08;如計算機科學與技術、人工智能、大數據等&#xff09;已成為考研競爭最…

python-leetcode 45.二叉樹轉換為鏈表

題目&#xff1a; 給定二叉樹的根節點root,請將它展開為一個單鏈表&#xff1a; 展開后的單鏈表應該使用同樣的TreeNode,其中right子指針指向鏈表中的下一個節點&#xff0c;而左子指針始終為空 展開后的單鏈表應該與二叉樹先序遍歷順序相同 方法一&#xff1a;二叉樹的前序…