国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - java實現 二叉搜索樹功能

java實現 二叉搜索樹功能

2021-05-14 10:31不懂是非 Java教程

這篇文章主要介紹了java實現 二叉搜索樹功能,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下

一、概念

  二叉搜索樹也成二叉排序樹,它有這么一個特點,某個節點,若其有兩個子節點,則一定滿足,左子節點值一定小于該節點值,右子節點值一定大于該節點值,對于非基本類型的比較,可以實現comparator接口,在本文中為了方便,采用了int類型數據進行操作。

  要想實現一顆二叉樹,肯定得從它的增加說起,只有把樹構建出來了,才能使用其他操作。

二、二叉搜索樹構建

   談起二叉樹的增加,肯定先得構建一個表示節點的類,該節點的類,有這么幾個屬性,節點的值,節點的父節點、左節點、右節點這四個屬性,代碼如下

java" id="highlighter_66631">
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static class node{
    node parent;
    node leftchild;
    node rightchild;
    int val;
    public node(node parent, node leftchild, node rightchild,int val) {
      super();
      this.parent = parent;
      this.leftchild = leftchild;
      this.rightchild = rightchild;
      this.val = val;
    }
    public node(int val){
      this(null,null,null,val);
    }
    public node(node node,int val){
      this(node,null,null,val);
    }
  }

        這里采用的是內部類的寫法,構建完節點值后,再對整棵樹去構建,一棵樹,先得有根節點,再能延伸到余下子節點,那在這棵樹里,也有一些屬性,比如基本的根節點root,樹中元素大小size,這兩個屬性,如果采用了泛型,可能還得增加comparator屬性,或提供其一個默認實現。具體代碼如下

?
1
2
3
4
5
6
7
public class searchbinarytree {
  private node root;
  private int size;
  public searchbinarytree() {
    super();
  }
}

三、增加

    當要進行添加元素的時候,得考慮根節點的初始化,一般情況有兩種、當該類的構造函數一初始化就對根節點root進行初始化,第二種、在進行第一次添加元素的時候,對根節點進行添加。理論上兩個都可以行得通,但通常采用的是第二種懶加載形式。

    在進行添加元素的時候,有這樣幾種情況需要考慮

       一、添加時判斷root是否初始化,若沒初始化,則初始化,將該值賦給根節點,size加一。

       二、因為二叉樹搜索樹滿足根節點值大于左節點,小于右節點,需要將插入的值,先同根節點比較,若大,則往右子樹中進行查找,若小,則往左子樹中進行查找。直到某個子節點。

       這里的插入實現,可以采用兩種,一、遞歸、二、迭代(即通過while循環模式)。

  3.1、遞歸版本插入

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public boolean add(int val){
    if(root == null){
      root = new node(val);
      size++;
      return true;
    }
    node node = getadapternode(root, val);
    node newnode = new node(val);
    if(node.val > val){
      node.leftchild = newnode;
      newnode.parent = node;
    }else if(node.val < val){
      node.rightchild = newnode;
      newnode.parent = node;
    }else{
      // 暫不做處理
    }
    size++;19     return true;
  }
  /**
   * 獲取要插入的節點的父節點,該父節點滿足以下幾種狀態之一
   * 1、父節點為子節點
   * 2、插入節點值比父節點小,但父節點沒有左子節點
   * 3、插入節點值比父節點大,但父節點沒有右子節點
   * 4、插入節點值和父節點相等。
   * 5、父節點為空
   * 如果滿足以上5種情況之一,則遞歸停止。
   * @param node
   * @param val
   * @return
   */
  private node getadapternode(node node,int val){
    if(node == null){
      return node;
    }
    // 往左子樹中插入,但沒左子樹,則返回
    if(node.val > val && node.leftchild == null){
      return node;
    }
    // 往右子樹中插入,但沒右子樹,也返回
    if(node.val < val && node.rightchild == null){
      return node;
    }
    // 該節點是葉子節點,則返回
    if(node.leftchild == null && node.rightchild == null){
      return node;
    }
    if(node.val > val && node.leftchild != null){
      return getadaptarnode(node.leftchild, val);
    }else if(node.val < val && node.rightchild != null){
      return getadaptarnode(node.rightchild, val);
    }else{
      return node;
    }
  }

   使用遞歸,先找到遞歸的結束點,再去把整個問題化為子問題,在上述代碼里,邏輯大致是這樣的,先判斷根節點有沒有初始化,沒初始化則初始化,完成后返回,之后通過一個函數去獲取適配的節點。之后進行插入值。

3.2、迭代版本

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public boolean put(int val){
    return putval(root,val);
  }
  private boolean putval(node node,int val){
    if(node == null){// 初始化根節點
      node = new node(val);
      root = node;
      size++;
      return true;
    }
    node temp = node;
    node p;
    int t;
    /**
     * 通過do while循環迭代獲取最佳節點,
     */
    do{
      p = temp;
      t = temp.val-val;
      if(t > 0){
        temp = temp.leftchild;
      }else if(t < 0){
        temp = temp.rightchild;
      }else{
        temp.val = val;
        return false;
      }
    }while(temp != null);
    node newnode = new node(p, val);
    if(t > 0){
      p.leftchild = newnode;
    }else if(t < 0){
      p.rightchild = newnode;
    }
    size++;
    return true;
  }

原理其實和遞歸一樣,都是獲取最佳節點,在該節點上進行操作。

論起性能,肯定迭代版本最佳,所以一般情況下,都是選擇迭代版本進行操作數據。

四、刪除

    可以說在二叉搜索樹的操作中,刪除是最復雜的,要考慮的情況也相對多,在常規思路中,刪除二叉搜索樹的某一個節點,肯定會想到以下四種情況,

 java實現 二叉搜索樹功能

   1、要刪除的節點沒有左右子節點,如上圖的d、e、g節點

   2、要刪除的節點只有左子節點,如b節點

   3、要刪除的節點只有右子節點,如f節點

   4、要刪除的節點既有左子節點,又有右子節點,如 a、c節點

對于前面三種情況,可以說是比較簡單,第四種復雜了。下面先來分析第一種

 若是這種情況,比如 刪除d節點,則可以將b節點的左子節點設置為null,若刪除g節點,則可將f節點的右子節點設置為null。具體要設置哪一邊,看刪除的節點位于哪一邊。

第二種,刪除b節點,則只需將a節點的左節點設置成d節點,將d節點的父節點設置成a即可。具體設置哪一邊,也是看刪除的節點位于父節點的哪一邊。

第三種,同第二種。

第四種,也就是之前說的有點復雜,比如要刪除c節點,將f節點的父節點設置成a節點,f節點左節點設置成e節點,將a的右節點設置成f,e的父節點設置f節點(也就是將f節點替換c節點),還有一種,直接將e節點替換c節點。那采用哪一種呢,如果刪除節點為根節點,又該怎么刪除?

 對于第四種情況,可以這樣想,找到c或者a節點的后繼節點,刪除后繼節點,且將后繼節點的值設置為c或a節點的值。先來補充下后繼節點的概念。

一個節點在整棵樹中的后繼節點必滿足,大于該節點值得所有節點集合中值最小的那個節點,即為后繼節點,當然,也有可能不存在后繼節點。

但是對于第四種情況,后繼節點一定存在,且一定在其右子樹中,而且還滿足,只有一個子節點或者沒有子節點兩者情況之一。具體原因可以這樣想,因為后繼節點要比c節點大,又因為c節點左右子節一定存在,所以一定存在右子樹中的左子節點中。就比如c的后繼節點是f,a的后繼節點是e。

有了以上分析,那么實現也比較簡單了,代碼如下

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public boolean delete(int val){
    node node = getnode(val);
    if(node == null){
      return false;
    }
    node parent = node.parent;
    node leftchild = node.leftchild;
    node rightchild = node.rightchild;
    //以下所有父節點為空的情況,則表明刪除的節點是根節點
    if(leftchild == null && rightchild == null){//沒有子節點
      if(parent != null){
        if(parent.leftchild == node){
          parent.leftchild = null;
        }else if(parent.rightchild == node){
          parent.rightchild = null;
        }
      }else{//不存在父節點,則表明刪除節點為根節點
        root = null;
      }
      node = null;
      return true;
    }else if(leftchild == null && rightchild != null){// 只有右節點
      if(parent != null && parent.val > val){// 存在父節點,且node位置為父節點的左邊
        parent.leftchild = rightchild;
      }else if(parent != null && parent.val < val){// 存在父節點,且node位置為父節點的右邊
        parent.rightchild = rightchild;
      }else{
        root = rightchild;
      }
      node = null;
      return true;
    }else if(leftchild != null && rightchild == null){// 只有左節點
      if(parent != null && parent.val > val){// 存在父節點,且node位置為父節點的左邊
        parent.leftchild = leftchild;
      }else if(parent != null && parent.val < val){// 存在父節點,且node位置為父節點的右邊
        parent.rightchild = leftchild;
      }else{
        root = leftchild;
      }
      return true;
    }else if(leftchild != null && rightchild != null){// 兩個子節點都存在
      node successor = getsuccessor(node);// 這種情況,一定存在后繼節點
      int temp = successor.val;
      boolean delete = delete(temp);
      if(delete){
        node.val = temp;
      }
      successor = null;
      return true;
    }
    return false;
  }
  
  /**
   * 找到node節點的后繼節點
   * 1、先判斷該節點有沒有右子樹,如果有,則從右節點的左子樹中尋找后繼節點,沒有則進行下一步
   * 2、查找該節點的父節點,若該父節點的右節點等于該節點,則繼續尋找父節點,
   *  直至父節點為null或找到不等于該節點的右節點。
   * 理由,后繼節點一定比該節點大,若存在右子樹,則后繼節點一定存在右子樹中,這是第一步的理由
   *   若不存在右子樹,則也可能存在該節點的某個祖父節點(即該節點的父節點,或更上層父節點)的右子樹中,
   *   對其迭代查找,若有,則返回該節點,沒有則返回null
   * @param node
   * @return
   */
  private node getsuccessor(node node){
    if(node.rightchild != null){
      node rightchild = node.rightchild;
      while(rightchild.leftchild != null){
        rightchild = rightchild.leftchild;
      }
      return rightchild;
    }
    node parent = node.parent;
    while(parent != null && (node == parent.rightchild)){
      node = parent;
      parent = parent.parent;
    }
    return parent;
  }

具體邏輯,看上面分析,這里不作文字敘述了,

除了這種實現,在算法導論書中,提供了另外一種實現。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public boolean remove(int val){
    node node = getnode(val);
    if(node == null){
      return false;
    }
    if(node.leftchild == null){// 1、左節點不存在,右節點可能存在,包含兩種情況 ,兩個節點都不存在和只存在右節點
      transplant(node, node.rightchild);
    }else if(node.rightchild == null){//2、左孩子存在,右節點不存在
      transplant(node, node.leftchild);
    }else{// 3、兩個節點都存在
      node successor = getsuccessor(node);// 得到node后繼節點
      if(successor.parent != node){// 后繼節點存在node的右子樹中。
        transplant(successor, successor.rightchild);// 用后繼節點的右子節點替換該后繼節點
        successor.rightchild = node.rightchild;// 將node節點的右子樹賦給后繼節點的右節點,即類似后繼與node節點調換位置
        successor.rightchild.parent = successor;// 接著上一步 給接過來的右節點的父引用復制
      }
      transplant(node, successor);
      successor.leftchild = node.leftchild;
      successor.leftchild.parent = successor;
    }
    return true;
  }
  /**
   * 將child節點替換node節點
   * @param root  根節點
   * @param node  要刪除的節點
   * @param child  node節點的子節點
   */
  private void transplant(node node,node child){
    /**
     * 1、先判斷 node是否存在父節點
     *  1、不存在,則child替換為根節點
     *  2、存在,則繼續下一步
     * 2、判斷node節點是父節點的那個孩子(即判斷出 node是右節點還是左節點),
     *  得出結果后,將child節點替換node節點 ,即若node節點是左節點 則child替換后 也為左節點,否則為右節點
     * 3、將node節點的父節點置為child節點的父節點
     */
    
    if(node.parent == null){
      this.root = child;
    }else if(node.parent.leftchild == node){
      node.parent.leftchild = child;
    }else if(node.parent.rightchild == node){
      node.parent.rightchild = child;
    }
    if(child != null){
      child.parent = node.parent;
    }
  }

五、查找

  查找也比較簡單,其實在增加的時候,已經實現了。實際情況中,這部分可以抽出來單獨方法。代碼如下

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public node getnode(int val){
    node temp = root;
    int t;
    do{
      t = temp.val-val;
      if(t > 0){
        temp = temp.leftchild;
      }else if(t < 0){
        temp = temp.rightchild;
      }else{
        return temp;
      }
    }while(temp != null);
    return null;
  }

六、二叉搜索樹遍歷

  在了解二叉搜索樹的性質后,很清楚的知道,它的中序遍歷是從小到大依次排列的,這里提供中序遍歷代碼

?
1
2
3
4
5
6
7
8
9
10
public void print(){
    print(root);
  }
  private void print(node root){
    if(root != null){
      print(root.leftchild);
      system.out.println(root.val);// 位置在中間,則中序,若在前面,則為先序,否則為后續
      print(root.rightchild);
    }
  }

總結

以上所述是小編給大家介紹的java實現 二叉搜索樹功能,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!

原文鏈接:https://www.cnblogs.com/qm-article/p/9279655.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 免费看男女www网站入口在线 | 欧美高清视频在线观看 | 国内外成人激情免费视频 | 国产美女久久 | 精品一区二区三区蜜桃 | 亚州中文| 亚洲精品无码专区在线播放 | 99久久免费视频在线观看 | 久久合| 黄色片免费在线观看视频 | 中文字幕亚洲欧美日韩在线不卡 | 国产精品1| 亚洲国产精品久久久久秋霞蜜臀 | 色婷婷导航 | 一区视频在线播放 | 国产精品久久久久久久久久久久久 | 国产综合精品 | 国产精品毛片久久久久久久 | 亚洲成人免费影院 | 久久久久久国产精品 | 奇米亚洲午夜久久精品 | 久久伊99综合婷婷久久伊 | 亚洲午夜视频在线观看 | 亚洲日本va中文字幕 | 婷婷综合激情 | 国产精品久久久久久久久久大牛 | 日韩中文字幕一区 | 国产精品一卡二卡 | 久久91精品| 在线午夜| 一级电影在线观看 | 国产成人精品综合 | www.一区| 日韩精品免费一区二区三区 | 国产精品一区二区久久久 | 日韩欧美在线综合 | 国产欧美日韩综合精品一区二区 | 日韩av免费在线观看 | 欧美精品不卡 | 欧美精品一区在线 | 91精品一区|