利用java實(shí)現(xiàn)二叉搜索樹(shù)
二叉搜索樹(shù)的定義
- 它是一顆二叉樹(shù)
- 任一節(jié)點(diǎn)的左子樹(shù)上的所有節(jié)點(diǎn)的值一定小于該節(jié)點(diǎn)的值
- 任一節(jié)點(diǎn)的右子樹(shù)上的所有節(jié)點(diǎn)的值一定大于該節(jié)點(diǎn)的值

特點(diǎn): 二叉搜索樹(shù)的中序遍歷結(jié)果是有序的(升序)!
實(shí)現(xiàn)一顆二叉搜索樹(shù)
- 實(shí)現(xiàn)二叉搜索樹(shù),將實(shí)現(xiàn)插入,刪除,查找三個(gè)方面
- 二叉搜索樹(shù)的節(jié)點(diǎn)是不可以進(jìn)行修改的,如果修改,則可能會(huì)導(dǎo)致搜索樹(shù)的錯(cuò)誤
二叉搜索樹(shù)的定義類
- 二叉搜索樹(shù)的節(jié)點(diǎn)類 ——
class Node - 二叉搜索樹(shù)的屬性:要找到一顆二叉搜索樹(shù)只需要知道這顆樹(shù)的根節(jié)點(diǎn)。
public class BST {
static class Node {
private int key;
private Node left;
private Node right;
public Node(int key) {
this.key = key;
}
}
private Node root;//BST的根節(jié)點(diǎn)
}
二叉搜索樹(shù)的查找
- 二叉搜索樹(shù)的查找思路:
- ①如果要查找的值等于當(dāng)前節(jié)點(diǎn)的值,那么,就找到了
- ②如果要查找的值小于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的左子樹(shù)走
- ③如果要查找的值大于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的右子樹(shù)走
- 最終,如果走到空了還沒(méi)有找到,就說(shuō)明不存在這個(gè)
key
/**
* 查找是否存在節(jié)點(diǎn)
*
* 思路:根據(jù)二叉排序樹(shù)的特點(diǎn):
* ①如果要查找的值小于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的左子樹(shù)走
* ②如果要查找的值大于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的右子樹(shù)走
*
* @param key 帶查找的key
* @return boolean是否存在
*/
public boolean find(int key) {
Node cur = root;
while (cur != null) {
if (key < root.key) {
cur = cur.left;
} else if (key > root.key) {
cur = cur.right;
} else {
return true;
}
}
return false;
}
二叉搜索樹(shù)的插入
- 二叉搜索樹(shù)的插入思路:
- 思路和查找一樣的,只是我們這次要進(jìn)行的是插入操作,那么我們還需要一個(gè)
parent節(jié)點(diǎn),來(lái)時(shí)刻記錄當(dāng)前節(jié)點(diǎn)的雙親節(jié)點(diǎn)即: - ①如果要插入的值等于當(dāng)前節(jié)點(diǎn)的值,那么,無(wú)法插入(不可出現(xiàn)重復(fù)的
key) - ②如果要插入的值小于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的左子樹(shù)走
- ③如果要插入的值大于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的右子樹(shù)走
- 最終,如果走到空了,就說(shuō)明不存在重復(fù)的
key,只要往雙親節(jié)點(diǎn)的后面插就好了,就是合適的位置,具體往左邊還是右邊插入,需要比較待插入節(jié)點(diǎn)的key和parent的key
/**
* 往二叉樹(shù)中插入節(jié)點(diǎn)
*
* 思路如下:
*
* @param key 待插入的節(jié)點(diǎn)
*/
public void insert(int key) {
if (root == null) { //如果是空樹(shù),那么,直接插入
root = new Node(key);
return;
}
Node cur = root;
Node parent = null; //parent 為cur的父節(jié)點(diǎn)
while (true) {
if (cur == null) { //在遍歷過(guò)程中,找到了合適是位置,就指針插入(沒(méi)有重復(fù)節(jié)點(diǎn))
if (parent.key < key) {
parent.right = new Node(key);
} else {
parent.left = new Node(key);
}
return;
}
if (key < cur.key) {
parent = cur;
cur = cur.left;
} else if (key > cur.key) {
parent = cur;
cur = cur.right;
} else {
throw new RuntimeException("插入失敗,已經(jīng)存在key");
}
}
}
二叉搜索樹(shù)的刪除
- 二叉搜索樹(shù)的刪除思路:(詳細(xì)的思路看注釋)
- 首先,需要先找到是否存在
key節(jié)點(diǎn),如果存在,則刪除,如果不存在則刪除錯(cuò)誤 - 對(duì)于,如果存在,則分為三種情況:
- ①要?jiǎng)h除的節(jié)點(diǎn),沒(méi)有左孩子
Ⅰ:要?jiǎng)h除的節(jié)點(diǎn)為根節(jié)點(diǎn):root = delete.right;
Ⅱ:要?jiǎng)h除的節(jié)點(diǎn)為其雙親節(jié)點(diǎn)的左孩子:parent.left = delete.right;
Ⅲ:要?jiǎng)h除的節(jié)點(diǎn)為其雙親節(jié)點(diǎn)的右孩子:parent.right = delete.right;
- ②要?jiǎng)h除的節(jié)點(diǎn),沒(méi)有右孩子
Ⅰ:要?jiǎng)h除的節(jié)點(diǎn)為根節(jié)點(diǎn):root = delete.left;
Ⅱ:要?jiǎng)h除的節(jié)點(diǎn)為其雙親節(jié)點(diǎn)的左孩子:parent.left = delete.left;
Ⅲ:要?jiǎng)h除的節(jié)點(diǎn)為其雙親節(jié)點(diǎn)的右孩子:parent.right = delete.left;
- ③要?jiǎng)h除的節(jié)點(diǎn),既有左孩子又有右孩子:
此時(shí)我們需要找到整顆二叉樹(shù)中第一個(gè)大于待刪除節(jié)點(diǎn)的節(jié)點(diǎn),然后替換他倆的值,最后,把找到的節(jié)點(diǎn)刪除
Ⅰ:找到的節(jié)點(diǎn)的雙親節(jié)點(diǎn)為待刪除的節(jié)點(diǎn):delete.key = find.key; findParent.right = find.right;
Ⅱ:找到的節(jié)點(diǎn)的雙親節(jié)點(diǎn)不是待刪除的節(jié)點(diǎn):delete.key = find.key; findParent.left = find.right;
/**
* 刪除樹(shù)中節(jié)點(diǎn)
*
* 思路如下:
*
* @param key 待刪除的節(jié)點(diǎn)
*/
public void remove(int key) {
if (root == null) {
throw new RuntimeException("為空樹(shù),刪除錯(cuò)誤!");
}
Node cur = root;
Node parent = null;
//查找是否key節(jié)點(diǎn)的位置
while (cur != null) {
if (key < cur.key) {
parent = cur;
cur = cur.left;
} else if (key > cur.key) {
parent = cur;
cur = cur.right;
} else {
break;
}
}
if (cur == null) {
throw new RuntimeException("找不到key,輸入key不合法");
}
//cur 為待刪除的節(jié)點(diǎn)
//parent 為待刪除的節(jié)點(diǎn)的父節(jié)點(diǎn)
/*
* 情況1:如果待刪除的節(jié)點(diǎn)沒(méi)有左孩子
* 其中
* ①待刪除的節(jié)點(diǎn)有右孩子
* ②待刪除的節(jié)點(diǎn)沒(méi)有右孩子
* 兩種情況可以合并
*/
if (cur.left == null) {
if (cur == root) { //①如果要?jiǎng)h除的是根節(jié)點(diǎn)
root = cur.right;
} else if (cur == parent.left) { //②如果要?jiǎng)h除的是其父節(jié)點(diǎn)的左孩子
parent.left = cur.right;
} else { //③如果要?jiǎng)h除的節(jié)點(diǎn)為其父節(jié)點(diǎn)的右孩子
parent.right = cur.right;
}
}
/*
* 情況2:如果待刪除的節(jié)點(diǎn)沒(méi)有右孩子
*
* 其中:待刪除的節(jié)點(diǎn)必定存在左孩子
*/
else if (cur.right == null) { //①如果要?jiǎng)h除的是根節(jié)點(diǎn)
if (cur == root) {
root = cur.left;
} else if (cur == parent.left) { //②如果要?jiǎng)h除的是其父節(jié)點(diǎn)的左孩子
parent.left = cur.left;
} else { //③如果要?jiǎng)h除的節(jié)點(diǎn)為其父節(jié)點(diǎn)的右孩子
parent.right = cur.left;
}
}
/*
* 情況3:如果待刪除的節(jié)點(diǎn)既有左孩子又有右孩子
*
* 思路:
* 因?yàn)槭桥判蚨鏄?shù),要找到整顆二叉樹(shù)第一個(gè)大于該節(jié)點(diǎn)的節(jié)點(diǎn),只需要,先向右走一步,然后一路往最左走就可以找到了
* 因此:
* ①先向右走一步
* ②不斷向左走
* ③找到第一個(gè)大于待刪除的節(jié)點(diǎn)的節(jié)點(diǎn),將該節(jié)點(diǎn)的值,替換到待刪除的節(jié)點(diǎn)
* ④刪除找到的這個(gè)節(jié)點(diǎn)
* ⑤完成刪除
*
*/
else {
Node nextParent = cur; //定義父節(jié)點(diǎn),初始化就是待刪除的節(jié)點(diǎn)
Node next = cur.right; //定義next為當(dāng)前走到的節(jié)點(diǎn),最終目的是找到第一個(gè)大于待刪除的節(jié)點(diǎn)
while (next.left != null) {
nextParent = next;
next = next.left;
}
cur.key = next.key; //找到之后,完成值的替換
if (nextParent == cur) { //此時(shí)的父節(jié)點(diǎn)就是待刪除的節(jié)點(diǎn),那么說(shuō)明找到的節(jié)點(diǎn)為父節(jié)點(diǎn)的右孩子(因?yàn)榇藭r(shí)next只走了一步)
nextParent.right = next.right;
} else { //此時(shí)父節(jié)點(diǎn)不是待刪除的節(jié)點(diǎn),即next確實(shí)往左走了,且走到了頭.
nextParent.left = next.right;
}
}
}
到此這篇關(guān)于利用java實(shí)現(xiàn)二叉搜索樹(shù)的文章就介紹到這了,更多相關(guān)java二叉搜索樹(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JDK基于CAS實(shí)現(xiàn)原子類盤點(diǎn)解析
這篇文章主要為大家介紹了JDK基于CAS實(shí)現(xiàn)原子類盤點(diǎn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
java中關(guān)于文本文件的讀寫方法實(shí)例總結(jié)
這篇文章主要介紹了java中關(guān)于文本文件的讀寫方法,實(shí)例總結(jié)了Java針對(duì)文本文件讀寫的幾種常用方法,并對(duì)比了各個(gè)方法的優(yōu)劣及特點(diǎn),具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
如何將DeepSeek 集成到 Java 的 Spring Boot&
本文介紹了如何將DeepSeek集成到Java的SpringBoot項(xiàng)目中,包括準(zhǔn)備工作、集成步驟和示例說(shuō)明,感興趣的朋友一起看看吧2025-02-02
springboot+vue+elementsUI實(shí)現(xiàn)分角色注冊(cè)登錄界面功能
這篇文章主要給大家介紹了關(guān)于springboot+vue+elementsUI實(shí)現(xiàn)分角色注冊(cè)登錄界面功能的相關(guān)資料,Spring?Boot和Vue.js是兩個(gè)非常流行的開(kāi)源框架,可以用來(lái)構(gòu)建Web應(yīng)用程序,需要的朋友可以參考下2023-07-07
Springboot整合Spring Cloud Kubernetes讀取ConfigMap支持自動(dòng)刷新配置的教程
這篇文章主要介紹了Springboot整合Spring Cloud Kubernetes讀取ConfigMap支持自動(dòng)刷新配置,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
springboot中如何通過(guò)main方法調(diào)用service或dao
這篇文章主要介紹了springboot中如何通過(guò)main方法調(diào)用service或dao,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Spring Boot單元測(cè)試中使用mockito框架mock掉整個(gè)RedisTemplate的示例
今天小編就為大家分享一篇關(guān)于Spring Boot單元測(cè)試中使用mockito框架mock掉整個(gè)RedisTemplate的示例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12

