java數據結構_Map和Set_9.1

1. 搜索樹

1.1 概念

二叉搜索樹又稱二叉排序樹,它或者是一棵空樹,或者是具有以下性質的二叉樹:

  • 若它的左子樹不為空,則左子樹上所有的結點都小于根結點的值
  • 若它的右子樹不為空,則右子樹上所有的結點都大于根結點的值
  • 它的左右子樹也都分別是二叉搜索樹

如下圖,就是一棵二叉搜索樹,二叉搜索樹的中序遍歷就是升序排列的

1.2 操作-查找

因為二叉搜索樹天然就帶有小的在左,大的在右的順序,所以在查找的時候更加方便,類似于有天然的二分查找

代碼實現如下:

1.3 操作 - 插入

按照二叉搜索樹,小的在左邊,大的在右邊邏輯確定插入位置,插入新結點。因為要插入的結點最終都是稱為葉子結點,所以光定義一個cur來表示要插入的結點還不夠,還要定義一個parent結點來表示cur的父親結點。

代碼如下:

但上面代碼的邏輯性還是有問題的,插入操作,并沒有考慮到,如果要插入的樹是一棵空樹,即root == null的情況,會出現空指針的異常。

修正:

1.4 操作 - 刪除(難)

搜索二叉樹的刪除是會發生很尷尬的情況的

如下圖:

如果要刪除值為8的結點,是很方便的,可以將7結點的right連接到值為9的結點。

但如果刪除的是左邊的值為1的結點呢?

刪除了之后,3的left是連接值為2的結點呢?還是連接值為0的結點呢?這樣就會出現尷尬的情況,采用的方法是替換法(也稱替罪羊法),即可以在要刪除的結點的右子樹中,尋找一個值最小的葉子節點,換個方式講是,找到要刪除的結點的右子樹的最左邊子樹(有點繞,可以多多理解一下),然后用這個最左邊子樹的值來覆蓋要刪除的結點的值,然后將最左邊子樹的結點刪除。注意是覆蓋,并不是將要刪除的結點真的刪除了,最終刪除的結點是要刪除的結點的右子樹的最左邊子樹的結點。

舉例:

如果要刪除的結點是值為90的結點

先找到90結點的右子樹為值為120的結點,然后再找到右子樹120的最左邊子樹,即值為95的結點,用這個結點的值95來覆蓋掉要刪除的結點90的值,然后刪除值為95的這個結點,如何刪除呢?如果值為95的這個結點仍有有右子樹呢?(注意:值為95的這個結點不可能再有左子樹了,因為我們要在前面的操作中找的就是 要刪除的結點的右子樹的最左子樹)就把值為95的結點的右子樹連接到值為120的結點的left上即可。

(上面只是一種方式,是找到 要刪除結點 的 右子樹 的最左側子樹,同理, 也可以找到 要刪除的結點 的 左子樹 的最右側子樹,都相反一下即可,此處以右子樹的最左側左子樹來研究)

代碼實現:

設待刪除結點為cur,帶刪除結點的雙親結點為parent

在刪除中,有多種情況:

1.1情況:cur為root 且 cur.left == null 如下圖,此時直接將cur的left賦值為root即可

刪除后的效果:

1.2情況:cur不是root,cur.left == null,cur在左子樹上,即cur 是 parent.left 如下圖的效果,此時要刪除的結點是值為40的結點,直接parent.left = cur.right即可

1.3 情況:cur不是root,cur.left == null cur是parent.right,即cur在右子樹上,如下圖情況,則直接parent.right = cur.right即可

2.1情況 cur.right == null cur是root 則直接root = cur.left即可

2.2情況:cur不是root cur是parent.left? 且cur.right == null 則parent .left = cur.left

2.3 cur不是root cur是parent.right 且cur.right == null 則parent.right = cur.left


即使用前面所述的替代法。

代碼實現如下:

下面的代碼實現是前面的刪除邏輯

替代法因為要向下找到要刪除結點 的 右邊子樹 的 最左邊子樹,所以還需要一個尋找的邏輯,定義一個targetParent和target來尋找找最左子樹

當結束上面的while循環時候,即target指向的是最左子樹結點,然后cur.val = target.val進行覆蓋

如下圖所示:要刪除的結點是cur,t是要刪除節點 的 右邊結點 的 最左側結點,將cur的值覆蓋為95,然后將 t 結點刪掉(targetParent.left = target.right)(注意:刪除的時候不要直接targetParent.left = null 因為target可能有右節點 也可能 沒有右節點)

則代碼如下:

但上面代碼還是存在邏輯漏洞

如果是下圖的情況,cur為值為95的結點,在while循環結束后,t指向的是值為120的結點,直接執行targetParent.left = target.right 就亂套了

所以還應該加一個 if 條件 即:if(taregt == targeParent.left)的時候,才能targetParent.left = target.right;否則應該是targetParent.right = target.right;

則替代法的實現代碼應該是:

完整的刪除代碼如下:

 public void remove(int val) {TreeNode cur = root;TreeNode parent = null;while(cur != null) {if(val < cur.val) {parent = cur;cur = cur.left;} else if(val > cur.val) {parent = cur;cur = cur.right;} else {//刪除的邏輯removeNode(parent,cur);return;}}}private void removeNode(TreeNode parent, TreeNode cur) {if(cur.left == null) {if(cur == root) {root = cur.right;} else if(cur == parent.right) {parent.right = cur.right;} else {parent.left = cur.right;}} else if(cur.right == null) {if(cur == root) {root = cur.left;} else if(cur == parent.right) {parent.right = cur.left;} else {parent.left = cur.left;}} else {//替代法:TreeNode targetParent = cur;TreeNode target = cur.right;while(target.left != null) {targetParent = target;target = target.left;}cur.val = target.val;if(targetParent.left == target) {targetParent.left = target.right;} else {targetParent.right = target.right;}}}

1.5 性能分析:

插入和刪除的操作都必須先查找,所以查找的效率可以代表二叉搜索樹中各個操作的性能。

對有n個結點的二叉搜索樹,若每個元素查找的概率相等,則二叉搜索樹平均查找商都是結點在二叉搜索樹的函數,即節點越深,則比較的次數就越多。

但對于同一個關鍵碼集合,如果各關鍵碼插入的次序不同,可能得到不同結構的二叉搜索樹:

最好情況下:二叉搜索樹為完全二叉樹,平均比較次數為logN

最壞情況下:二叉搜索樹退化為單分支樹,其平均比較次數為:N / 2

可以及逆行改進,使得不管按照什么次序插入關鍵碼,都可以使得二叉搜索樹的性能最佳 -- 數據結構高階時候學習!

2. 搜索

2.1 概念及場景

Map和Set是一種專門用來進行搜索的容器或者數據結構,其搜索的效率與其具體的實例化子類有關。之前常見的搜索方式有:

  1. 直接遍歷:時間復雜度為O(N),元素如果比較多的話,效率較低。
  2. 二分查找:時間復雜度為O(logN),但搜索前必須要求序列是有序的

上述方式比較適合靜態類型的數據進行查找,即一般不會對區間內進行插入和刪除操作了,但在現實情況中的查找:1. 根據姓名查詢考試成績 2. 通訊錄,即根據姓名查詢聯系方式 3. 不重復集合,即需要先搜索關鍵字是否已經在集合中,可能在查找的過程中進行一些插入和刪除的操作,即動態查找,那上述兩種方式就不合適了,這里介紹的Map和Set是一種適合動態查找的集合容器

2.2 模型

一般把搜索的數據稱為關鍵字(Key),和關鍵字對應的稱為值(Value),將其稱之為Key-value的鍵值對,所以模型有兩種:

  1. 純Key模型,比如 有一個英文詞典,快速查找一個單詞是否收錄在該詞典中
  2. Key-Value模型,比如,梁山好漢的綽號

Map中存儲的就是key-value鍵值對,Set中只存儲了Key

3. Map的使用

Map官方文檔:Map (Java Platform SE 8 )


3.1 Map的說明:

Map是一個接口類,并沒有繼承自Collection,該類中存儲的是<K,V>結構的鍵值對,并且K一定是唯一的,不能重復

3.2 關于Map.Entry<K,V>的說明

Map.Entr<K,V>是Map內部實現的用來存放<key,value>鍵值對映射關系的內部類,該內部類中主要提供了key和value的獲取,value的設置以及key的比較方式

注意:Map.Entry<K,V>并沒有提供設置Key的方法

3.3 Map的常用方法說明

注意:

  1. Map是一個接口,不能直接實例化對象,如果要實例化對象是能實例化其實現類TreeMap或者HashMap
  2. Map中存放鍵值對的Key是唯一的value是可以重復的
  3. 在TreeMap中插入鍵值對時,key不能為空,否則會拋出空指針異常,但value可以為空,在HashMap中,key和value都可以為空
  4. Map中的Key可以全部分離出來,存儲到Set中來進行訪問(因為Key不能重復)
  5. Map中的value可以全部分離出來,存儲在Collection的任何一個子集中(value可能有重復)。
  6. Map中鍵值對的Key不能直接修改,value可以修改,如果要修改key,只能先將該key刪除掉,然后再來進行重新插入。

3.4 TreeMap的使用案例

getOrDefault方法的源碼:

完!

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

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

相關文章

Rust Async 并發編程:處理任意數量的 Future 與 Stream

1. Streams&#xff1a;異步數據流 1.1 Streams 與 Iterator 的異同 Rust 的 Iterator 是同步的&#xff0c;通過 next() 方法逐個獲取數據。而 Stream 是 async 版本的 Iterator&#xff0c;它使用 next().await 來獲取數據項。 示例&#xff1a;將 Iterator 轉換為 Stream…

藍橋杯 路徑之謎

路徑之謎 題目描述 小明冒充 XX 星球的騎士&#xff0c;進入了一個奇怪的城堡。 城堡里邊什么都沒有&#xff0c;只有方形石頭鋪成的地面。 假設城堡地面是 nnnn 個方格。如下圖所示。 按習俗&#xff0c;騎士要從西北角走到東南角。可以橫向或縱向移動&#xff0c;但不能斜著走…

3-5 WPS JS宏 工作表的移動與復制學習筆記

************************************************************************************************************** 點擊進入 -我要自學網-國內領先的專業視頻教程學習網站 *******************************************************************************************…

聊聊Java的SPI機制

個人自建博客地址 什么是SPI呢&#xff1f; SPI全稱Service Provider Interface&#xff0c;翻譯過來就是服務提供者接口。調用方提供接口聲明&#xff0c;服務提供方對接口進行實現&#xff0c;提供服務的一種機制&#xff0c;服務提供方往往是第三方或者是外部擴展。 下面…

langchain4j+local-ai小試牛刀

序 本文主要研究一下如何本地運行local-ai并通過langchain4j集成調用。 步驟 curl安裝 curl https://localai.io/install.sh | sh% Total % Received % Xferd Average Speed Time Time Time CurrentDload Upload Total Spent Left Speed 100 21509 …

什么是“零日漏洞”(Zero-Day Vulnerability)?為何這類攻擊被視為高風險威脅?

正文 零日漏洞&#xff08;Zero-Day Vulnerability&#xff09; 是指軟件、硬件或系統中存在的、尚未被開發者發現或修復的安全漏洞。攻擊者在開發者意識到漏洞存在之前&#xff08;即“零日”內&#xff09;利用該漏洞發起攻擊&#xff0c;因此得名。這類漏洞的“零日”特性使…

鴻蒙 ArkUI 實現 2048 小游戲

2048 是一款經典的益智游戲&#xff0c;玩家通過滑動屏幕合并相同數字的方塊&#xff0c;最終目標是合成數字 2048。本文基于鴻蒙 ArkUI 框架&#xff0c;詳細解析其實現過程&#xff0c;幫助開發者理解如何利用聲明式 UI 和狀態管理構建此類游戲。 一、核心數據結構與狀態管理…

Milvus高性能向量數據庫與大模型結合

Milvus | 高性能向量數據庫&#xff0c;為規模而構建Milvus 是一個為 GenAI 應用構建的開源向量數據庫。使用 pip 安裝&#xff0c;執行高速搜索&#xff0c;并擴展到數十億個向量。https://milvus.io/zh Milvus 是什么&#xff1f; Milvus 是一種高性能、高擴展性的向量數據…

kettle插件-自定義函數-數據脫敏

平常我們在使用kettle抽取數據的時候會涉及到敏感數據邀請脫敏或者進行掩碼的需求&#xff0c;今天我們使用自定義函數插件來實現這些需求。 1、將自定義函數插件&#xff08;kettle-func-plugin.zip&#xff09;解壓后放到kettle的plugins目錄下面&#xff0c;然后重啟服務。…

LeetCode 每日一題 2025/2/24-2025/3/2

記錄了初步解題思路 以及本地實現代碼&#xff1b;并不一定為最優 也希望大家能一起探討 一起進步 目錄 2/24 1656. 設計有序流2/25 2502. 設計內存分配器2/26 1472. 設計瀏覽器歷史記錄2/27 2296. 設計一個文本編輯器2/28 2353. 設計食物評分系統3/1 131. 分割回文串3/2 2/24 …

C++動態與靜態轉換區別詳解

文章目錄 前言一、 類型檢查的時機二、安全性三、適用場景四、代碼示例對比總結 前言 在 C 中&#xff0c;dynamic_cast 和 static_cast 是兩種不同的類型轉換操作符&#xff0c;主要區別體現在類型檢查的時機、安全性和適用場景上。以下是它們的核心區別&#xff1a; 一、 類…

探秘《矩陣之美》:解鎖矩陣的無限魅力

在這個數據驅動的時代&#xff0c;矩陣作為數學中的瑰寶&#xff0c;不僅在理論研究中占據核心地位&#xff0c;更在工程技術、計算機科學、物理學、經濟學等眾多領域發揮著不可替代的作用。今天&#xff0c;讓我們通過中科院大學耿修瑞老師&#xff08;中科院空天信息研究院研…

【MySQL】(2) 庫的操作

SQL 關鍵字&#xff0c;大小寫不敏感。 一、查詢數據庫 show databases; 注意加分號&#xff0c;才算一句結束。 二、創建數據庫 {} 表示必選項&#xff0c;[] 表示可選項&#xff0c;| 表示任選其一。 示例&#xff1a;建議加上 if not exists 選項。 三、字符集編碼和排序…

Vue3實現文件上傳、下載及預覽全流程詳解(含完整接口調用)

文章目錄 一、環境準備1.1 創建Vue3項目1.2 安裝依賴1.3 配置Element Plus 二、文件上傳實現2.1 基礎上傳組件2.2 自定義上傳邏輯&#xff08;Axios實現&#xff09; 三、文件下載實現3.1 直接下載&#xff08;已知文件URL&#xff09;3.2 后端接口下載&#xff08;二進制流&am…

分布式數據存儲:提升系統彈性與性能的技術之路

分布式數據存儲:提升系統彈性與性能的技術之路 在當今數據爆炸式增長的時代,傳統的單機存儲系統已無法滿足大規模、高并發、低延遲的需求。尤其是在大數據、云計算和物聯網的推動下,數據存儲面臨著前所未有的挑戰。分布式數據存儲應運而生,通過將數據分布在多個物理節點上…

在編譯Linux的內核鏡像和模塊時,必須先編譯內核鏡像,再編譯模塊,順序不可隨意調整的原因

問&#xff1a;在編譯Linux的內核鏡像和模塊時,必須先編譯內核鏡像,再編譯模塊,順序不可隨意調整 答&#xff1a;在編譯 Linux 內核和模塊時&#xff0c;必須先編譯內核鏡像&#xff0c;再編譯模塊&#xff0c;順序不可隨意調整。 原因&#xff1a; 模塊依賴內核的頭文件和符…

免費使用 DeepSeek API 教程及資源匯總

免費使用 DeepSeek API 教程及資源匯總 一、DeepSeek API 資源匯總1.1 火山引擎1.2 百度千帆1.3 阿里百煉1.4 騰訊云 二、其他平臺2.1 華為云2.2 硅基流動 三、總結 DeepSeek-R1 作為 2025 年初發布的推理大模型&#xff0c;憑借其卓越的邏輯推理能力和成本優勢&#xff0c;迅速…

千峰React:案例二

完成對html文檔還有css的引入&#xff0c;引入一下數據&#xff1a; import { func } from prop-types import ./購物車樣式.css import axios from axios import { useImmer } from use-immer import { useEffect } from reactfunction Item() {return (<li classNameacti…

用DeepSeek生成批量刪除處理 PDF第一頁工具

安裝依賴庫 在運行程序之前&#xff0c;請確保安裝所需的庫&#xff1a; pip install pymupdf python-docx Python 程序代碼 import os import fitz # PyMuPDF from docx import Documentdef delete_pdf_first_page(input_path, output_path):"""刪除 PDF…

redis的下載和安裝詳解

一、下載redis安裝包 進入redis官網查看當前穩定版本&#xff1a; https://redis.io/download/發現此時的穩定版本是6.2.4&#xff0c; 此時可以去這個網站下載6.2.4穩定版本的tar包。 暫時不考慮不在windows上使用redis&#xff0c;那樣將無法發揮redis的性能 二、上傳tar…