利用java實(shí)現(xiàn)二叉搜索樹
二叉搜索樹的定義
- 它是一顆二叉樹
- 任一節(jié)點(diǎn)的左子樹上的所有節(jié)點(diǎn)的值一定小于該節(jié)點(diǎn)的值
- 任一節(jié)點(diǎn)的右子樹上的所有節(jié)點(diǎn)的值一定大于該節(jié)點(diǎn)的值
特點(diǎn): 二叉搜索樹的中序遍歷結(jié)果是有序的(升序)!
實(shí)現(xiàn)一顆二叉搜索樹
- 實(shí)現(xiàn)二叉搜索樹,將實(shí)現(xiàn)插入,刪除,查找三個(gè)方面
- 二叉搜索樹的節(jié)點(diǎn)是不可以進(jìn)行修改的,如果修改,則可能會(huì)導(dǎo)致搜索樹的錯(cuò)誤
二叉搜索樹的定義類
- 二叉搜索樹的節(jié)點(diǎn)類 ——
class Node
- 二叉搜索樹的屬性:要找到一顆二叉搜索樹只需要知道這顆樹的根節(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) }
二叉搜索樹的查找
- 二叉搜索樹的查找思路:
- ①如果要查找的值等于當(dāng)前節(jié)點(diǎn)的值,那么,就找到了
- ②如果要查找的值小于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的左子樹走
- ③如果要查找的值大于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的右子樹走
- 最終,如果走到空了還沒有找到,就說明不存在這個(gè)
key
/** * 查找是否存在節(jié)點(diǎn) * * 思路:根據(jù)二叉排序樹的特點(diǎn): * ①如果要查找的值小于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的左子樹走 * ②如果要查找的值大于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的右子樹走 * * @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; }
二叉搜索樹的插入
- 二叉搜索樹的插入思路:
- 思路和查找一樣的,只是我們這次要進(jìn)行的是插入操作,那么我們還需要一個(gè)
parent
節(jié)點(diǎn),來時(shí)刻記錄當(dāng)前節(jié)點(diǎn)的雙親節(jié)點(diǎn)即: - ①如果要插入的值等于當(dāng)前節(jié)點(diǎn)的值,那么,無法插入(不可出現(xiàn)重復(fù)的
key
) - ②如果要插入的值小于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的左子樹走
- ③如果要插入的值大于當(dāng)前節(jié)點(diǎn)的值,那么,就往當(dāng)前節(jié)點(diǎn)的右子樹走
- 最終,如果走到空了,就說明不存在重復(fù)的
key
,只要往雙親節(jié)點(diǎn)的后面插就好了,就是合適的位置,具體往左邊還是右邊插入,需要比較待插入節(jié)點(diǎn)的key
和parent
的key
/** * 往二叉樹中插入節(jié)點(diǎn) * * 思路如下: * * @param key 待插入的節(jié)點(diǎn) */ public void insert(int key) { if (root == null) { //如果是空樹,那么,直接插入 root = new Node(key); return; } Node cur = root; Node parent = null; //parent 為cur的父節(jié)點(diǎn) while (true) { if (cur == null) { //在遍歷過程中,找到了合適是位置,就指針插入(沒有重復(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"); } } }
二叉搜索樹的刪除
- 二叉搜索樹的刪除思路:(詳細(xì)的思路看注釋)
- 首先,需要先找到是否存在
key
節(jié)點(diǎn),如果存在,則刪除,如果不存在則刪除錯(cuò)誤 - 對(duì)于,如果存在,則分為三種情況:
- ①要?jiǎng)h除的節(jié)點(diǎn),沒有左孩子
Ⅰ:要?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),沒有右孩子
Ⅰ:要?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í)我們需要找到整顆二叉樹中第一個(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;
/** * 刪除樹中節(jié)點(diǎn) * * 思路如下: * * @param key 待刪除的節(jié)點(diǎn) */ public void remove(int key) { if (root == null) { throw new RuntimeException("為空樹,刪除錯(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)沒有左孩子 * 其中 * ①待刪除的節(jié)點(diǎn)有右孩子 * ②待刪除的節(jié)點(diǎn)沒有右孩子 * 兩種情況可以合并 */ 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)沒有右孩子 * * 其中:待刪除的節(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)槭桥判蚨鏄?要找到整顆二叉樹第一個(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),那么說明找到的節(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)二叉搜索樹的文章就介紹到這了,更多相關(guān)java二叉搜索樹內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA修改java文件后 不用重啟Tomcat服務(wù)便可實(shí)現(xiàn)自動(dòng)更新
這篇文章主要介紹了IDEA修改java文件后 不用重啟Tomcat服務(wù)便可實(shí)現(xiàn)自動(dòng)更新,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Java實(shí)現(xiàn)多線程下載和斷點(diǎn)續(xù)傳
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)多線程下載和斷點(diǎn)續(xù)傳,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06Spring Boot整合Spring Security簡單實(shí)現(xiàn)登入登出從零搭建教程
這篇文章主要給大家介紹了關(guān)于Spring Boot整合Spring Security簡單實(shí)現(xiàn)登入登出從零搭建的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧2018-09-09Java泛型在集合使用與自定義及繼承上的體現(xiàn)和通配符的使用
泛型又稱參數(shù)化類型,是Jdk5.0 出現(xiàn)的新特性,解決數(shù)據(jù)類型的安全性問題,在類聲明或?qū)嵗瘯r(shí)只要指定好需要的具體的類型即可。Java泛型可以保證如果程序在編譯時(shí)沒有發(fā)出警告,運(yùn)行時(shí)就不會(huì)產(chǎn)生ClassCastException異常。同時(shí),代碼更加簡潔、健壯2021-09-09SpringBoot如何返回頁面的實(shí)現(xiàn)方法
SpringBoot中使用Controller和頁面的結(jié)合能夠很好地實(shí)現(xiàn)用戶的功能及頁面數(shù)據(jù)的傳遞。本文介紹了如何實(shí)現(xiàn)頁面的返回以及這里面所包含的坑,感興趣的可以了解一下2021-07-07