最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java數(shù)據(jù)結(jié)構(gòu)超詳細(xì)分析二叉搜索樹

 更新時間:2022年03月21日 09:17:27   作者:未見花聞  
二叉搜索樹是以一棵二叉樹來組織的。每個節(jié)點是一個對象,包含的屬性有l(wèi)eft,right,p和key,其中,left指向該節(jié)點的左孩子,right指向該節(jié)點的右孩子,p指向該節(jié)點的父節(jié)點,key是它的值

封面

1.搜索樹的概念

二叉搜索樹是一種特殊的二叉樹,又稱二叉查找樹,二叉排序樹,它有幾個特點:

  • 如果左子樹存在,則左子樹每個結(jié)點的值均小于根結(jié)點的值。
  • 如果右子樹存在,則右子樹每個結(jié)點的值均大于根結(jié)點的值。
  • 中序遍歷二叉搜索樹,得到的序列是依次遞增的。
  • 二叉搜索樹的左右子樹均為二叉搜索樹。
  • 二叉搜索樹的結(jié)點的值不能發(fā)生重復(fù)。

1-1

2.二叉搜索樹的簡單實現(xiàn)

我們來簡單實現(xiàn)以下搜索樹,就不使用泛型了,二叉搜索樹基本結(jié)構(gòu):

public class BinarySearchTree {

    static class Node {
        public int val;
        public Node left;
        public Node right;
        public Node(int val) {
            this.val = val;
        }
    }

    public Node root;
    //其他方法
}

2.1查找

二叉搜索樹最擅長的就是查找,根據(jù)二叉搜索樹的定義,左子樹的元素比根小,右子樹的元素比根大,所以我們只需要根據(jù)根結(jié)點的值與目標(biāo)元素的值比較,就能實現(xiàn)查找功能。

  • 根與目標(biāo)元素相等,表示找到了。
  • 根比目標(biāo)元素大,去左子樹找。
  • 根比目標(biāo)元素小,去右子樹找。
  • 左右子樹找不到,那就找不到了。

參考實現(xiàn)代碼:

    public Node search(int key) {
        Node cur = this.root;
        while (cur != null) {
            //根與目標(biāo)元素相等,表示找到了。
            if (cur.val == key) return cur;
            //根比目標(biāo)元素大,去左子樹找。
            else if (cur.val > key) cur = cur.left;
            //根比目標(biāo)元素小,去右子樹找。
            else cur = cur.right;
        }
        //此時cur = null, 左右子樹找不到,那就找不到了。
        return cur;
    }

2.2插入

需要在二叉搜索樹中插入一個元素,首先得找到一個合適的插入位置,如何找呢?其實就是利用搜索樹查找的方式,找到一個空位,如何將目標(biāo)結(jié)點插入到這個位置。

  • 根與插入元素相等,插入元素不能與搜索樹中的元素相等,插入失敗。
  • 根比插入元素大,去左子樹找。
  • 根比插入元素小,去右子樹找。
  • 找到的結(jié)點為空,那這個位置就是我們要找的空位。

2-1

由于你找到空位時,無法獲取該空位的前一個位置,所以每次查找的時候都需要保存上一次查找的位置。

找到位置后,將目標(biāo)結(jié)點插入到該位置。

2-2

參考實現(xiàn)代碼:

    public boolean insert(int val) {
        //結(jié)點為空,直接插
        if(root == null) {
            root = new Node(val);
            return true;
        }
        Node cur = this.root;   //當(dāng)前查找位置
        Node parent = null;     //查找的上一個位置
        while (cur != null) {
            parent = cur;
            if (val > cur.val) cur = cur.right;
            else if (val < cur.val) cur = cur.left;
            else return false;
        }
        //開始插入,找到空位前一個位置,比插入元素小,空位在右邊,插入右邊
        if (val > parent.val) {
            parent.right = new Node(val);
        } else {
            //比插入元素大,空位在左邊,插入左邊
            parent.left = new Node(val);
        }
        return true;
    }

2.3刪除

刪除是搜索樹基本操作中最麻煩的一個操作,需要考慮多種情況。

不妨設(shè)需要刪除的結(jié)點為cur,cur的父結(jié)點為parent,搜索樹的根結(jié)點為root。首先需要刪除結(jié)點,那就得找到結(jié)點,所以第一步是找結(jié)點,思路與查找的思路一模一樣。

第二步那就是刪除了,刪除結(jié)點大概有下面幾種情況:

情況1:cur.left == null

  • cur == root,讓root = cur.right;
  • cur != root且parent.left == cur,讓parent.left = cur.right;
  • cur != root且parent.right == cur,讓parent.right = cur.right。

情況2:cur.right == null

  • cur == null,讓root = cur.left;
  • cur != root且parent.left == cur,讓parent.left = cur.left;
  • cur != root且parent.right == cur,讓parent.right = cur.left。

情況3:cur.left != null && cur.right != null

方案1:找到cur右子樹中最小的元素target,然后將該元素的值覆蓋到cur處(可以理解為交換),此時等價于刪除target處的結(jié)點,即該結(jié)點的父結(jié)點為preTarget

3-1

3-2

因為targetcur右子樹最小的一個結(jié)點,所以target.left == null,此時preTarget.left == target,所以刪除時按照上面的情況1去進(jìn)行刪除。

3-2-1

但是還有一種特殊情況,那就是cur.right就是最小結(jié)點,此時preTarget==cur,即preTarget.right == target,這時刪除時要將 preTarget.right = target.right。

3-3

3-4

3-4--0

方案2:找到cur左子樹中最大的元素target,然后將該元素的值覆蓋到cur處(可以理解為交換),此時等價于刪除target處的結(jié)點,即該結(jié)點的父結(jié)點為preTarget。

4-1

因為targetcur左子樹最大的一個結(jié)點,所以target.right == null,此時preTarget.right == target,所以刪除時按照上面的情況2去進(jìn)行刪除。

4-2

但是還有一種特殊情況,那就是cur.left就是左子樹最大結(jié)點,此時preTarget==cur,即preTarget.left == target,這時刪除時要將 preTarget.left = target.left。

4-3

4-4

參考實現(xiàn)代碼:

    public void remove(int key) {
        Node cur = root;
        Node parent = null;
        while (cur != null) {
            if(cur.val == key) {
                //這里開始刪除
                removeNode(cur,parent);
                break;
            }else if(cur.val < key) {
                parent = cur;
                cur = cur.right;
            }else {
                parent = cur;
                cur = cur.left;
            }
        }
    }

removeNode方法(方案1):

    public void removeNode(Node cur,Node parent) {
        if(cur.left == null) {
            if(cur == root) {
                root = cur.right;
            }else if(cur == parent.left) {
                parent.left = cur.right;
            }else {
                parent.right = cur.right;
            }
        }else if(cur.right == null) {
            if(cur == root) {
                root = cur.left;
            }else if(cur == parent.left) {
                parent.left = cur.left;
            }else {
                parent.right = cur.left;
            }
        }else {
            Node preTarget  = cur ;
            Node target  = cur.right;
            while (target.left != null) {
                preTarget = target;
                target = target.left;
            }
            cur.val = target.val;
            if (target == preTarget.left) {
                preTarget.left = target.right;
            } else {
                preTarget.right = target.right;
            }
        }
    }

removeNode方法(方案2):

    public void removeNode(Node cur,Node parent) {
        if(cur.left == null) {
            if(cur == root) {
                root = cur.right;
            }else if(cur == parent.left) {
                parent.left = cur.right;
            }else {
                parent.right = cur.right;
            }
        }else if(cur.right == null) {
            if(cur == root) {
                root = cur.left;
            }else if(cur == parent.left) {
                parent.left = cur.left;
            }else {
                parent.right = cur.left;
            }
        }else {
            Node preTarget  = cur ;
            Node target  = cur.left;
            while (target.right != null) {
                preTarget = target;
                target = target.right;
            }
            cur.val = target.val;
            if (target == preTarget.left) {
                preTarget.left = target.left;
            } else {
                preTarget.right = target.left;
            }
        }
    }

2.4修改

搜索樹的修改可以基于刪除和插入,先刪除目標(biāo)元素,然后再插入修改元素。

參考實現(xiàn)代碼:

    public void set(int key, int val) {
        remove(key);
        insert(val);
    }

3.二叉搜索樹的性能

在平衡二叉樹的情況下(左右子樹高度差不超過1),假設(shè)有n個結(jié)點,此時時間復(fù)雜度為二叉樹的高度,即 O ( l o g 2 n ) O(log_2n) O(log2?n),但是這只是例行情況,最不理想的情況就是二叉樹化為單分支樹,時間復(fù)雜為 O ( n ) O(n) O(n)。

為了解決這個問題,后面引申出AVL樹,紅黑樹,其中TreeMap與TreeSet的底層就是紅黑樹。具體紅黑樹是什么,這里就不多說了。

本文到底了,你學(xué)會了嗎?

到此這篇關(guān)于Java數(shù)據(jù)結(jié)構(gòu)超詳細(xì)分析二叉搜索樹的文章就介紹到這了,更多相關(guān)Java 二叉搜索樹內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解jvm雙親委派機制

    詳解jvm雙親委派機制

    雙親委派機制保證了核心類的安全,確保不會被修改,也保證了不會加載到重復(fù)的字節(jié)碼文件,這篇文章主要介紹了jvm雙親委派機制詳解,需要的朋友可以參考下
    2022-11-11
  • list,set,map,數(shù)組之間的相互轉(zhuǎn)換詳細(xì)解析

    list,set,map,數(shù)組之間的相互轉(zhuǎn)換詳細(xì)解析

    以下是對Java中l(wèi)ist,set,map,數(shù)組之間的相互轉(zhuǎn)換進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下
    2013-09-09
  • Nacos設(shè)置為windows自啟動服務(wù)的步驟詳解

    Nacos設(shè)置為windows自啟動服務(wù)的步驟詳解

    這篇文章給大家介紹了Nacos設(shè)置為windows自啟動服務(wù)的操作步驟,文中通過代碼示例和圖文結(jié)合講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-12-12
  • Elasticsearch中FST與前綴搜索應(yīng)用實戰(zhàn)解析

    Elasticsearch中FST與前綴搜索應(yīng)用實戰(zhàn)解析

    這篇文章主要為大家介紹了Elasticsearch中FST與前綴搜索應(yīng)用實戰(zhàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • 對dbunit進(jìn)行mybatis DAO層Excel單元測試(必看篇)

    對dbunit進(jìn)行mybatis DAO層Excel單元測試(必看篇)

    下面小編就為大家?guī)硪黄獙bunit進(jìn)行mybatis DAO層Excel單元測試(必看篇)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • Java設(shè)計模式之組合模式的示例詳解

    Java設(shè)計模式之組合模式的示例詳解

    組合模式,又叫部分整體模式,它創(chuàng)建了對象組的數(shù)據(jù)結(jié)構(gòu)組合模式使得用戶對單個對象和組合對象的訪問具有一致性。本文將通過示例為大家詳細(xì)介紹一下組合模式,需要的可以參考一下
    2022-03-03
  • IDEA類存在但找不到的解決辦法

    IDEA類存在但找不到的解決辦法

    本文主要介紹了IDEA類存在但找不到的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Java StringBuffer與StringBuilder有什么區(qū)別

    Java StringBuffer與StringBuilder有什么區(qū)別

    當(dāng)對字符串進(jìn)行修改的時候,需要使用 StringBuffer 和 StringBuilder類,和String類不同的是,StringBuffer和 StringBuilder類的對象能夠被多次的修改,并且不產(chǎn)生新的未使用對象,本篇我們來分析分析它們的區(qū)別
    2023-01-01
  • Java中Spring Boot支付寶掃碼支付及支付回調(diào)的實現(xiàn)代碼

    Java中Spring Boot支付寶掃碼支付及支付回調(diào)的實現(xiàn)代碼

    這篇文章主要介紹了Java中Spring Boot支付寶掃碼支付及支付回調(diào)的實現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02
  • 使用IDEA如何導(dǎo)入SpringBoot項目

    使用IDEA如何導(dǎo)入SpringBoot項目

    這篇文章主要介紹了使用IDEA如何導(dǎo)入SpringBoot項目問題,具有很好的參考價值,希望對大家有所幫助,
    2023-12-12

最新評論

化州市| 科技| 衡山县| 枞阳县| 哈密市| 阜新| 蕉岭县| 五指山市| 罗城| 穆棱市| 龙井市| 新竹县| 遂昌县| 寿宁县| 乌兰察布市| 平潭县| 苏州市| 长武县| 安阳县| 拜泉县| 离岛区| 沂南县| 三穗县| 亚东县| 长春市| 长兴县| 淮北市| 信阳市| 河源市| 当雄县| 抚宁县| 汝阳县| 兰州市| 尖扎县| 舟山市| 呼伦贝尔市| 厦门市| 诸城市| 龙江县| 沈阳市| 囊谦县|