二叉树介绍

二叉树能够用来作什么?node

答:能够搜索、排序程序员

但是,排序有快速排序、归并排序,查找有二分法、直接遍历等,那么为何要用二叉树呢?数组

二叉树确实在实际运用中比较少,由于有更高级的树,可是二叉树做为一种最基本最典型的排序树,是研究其余树的基础。数据结构

咱们知道,在有序数组中,能够快速找到特定的值;可是在有序数组内插入一个新数据项,或者删除数据项,须要费时的移动全部位置改变的数据项,因此在作插入和删除操做时,不应选用有序数组。post

另外一方面,链表中能够快速添加和删除某个数据项,可是在链表中查找数据不容易,必须从头开始访问链表的每个数据项,直到找到该数据为止,过程很慢。spa

树这种数据结构,技能像链表那样快速插入和删除,又能像有序数组那样快速查找。这里主要实现一种特殊的树--二叉(搜索)树。3d

二叉(搜索)树有以下特色:code

1. 一个子节点的关键字值小于这个节点,右子节点的关键字值大于或者等于这个节点。blog

2. 插入一个节点须要根据这个规则进行插入。排序

3. 删除节点时,二叉搜索树最复杂的操做,可是删除节点在不少树中的应用又很是重要,因此详细研究并总结以下特色。

  1)删除节点要从查找要删的节点开始入手,首先找到节点,这个要删除的节点可能有三种状况须要考虑:

  a. 该节点是叶节点,没有子节点;

  b. 该节点有一个子节点;

  c. 该节点有2个子节点;

 第一种最简单,第二种也比较简单,第三种就至关复杂了。下面分析这三种删除状况:

1、要删除叶节点,只须要改变该节点的父节点对应子字段的值便可,由指向该节点改成 null 就能够了。垃圾回收器会自动回收叶节点,不须要本身手动删掉。

2、当节点有一个子节点时,这个节点只有两个链接:连向父节点和连向它惟一的子节点。须要从这个序列中剪断这个节点,把它的子节点直接连到它的父节点上便可,这个过程要求改变父节点适当的引用(左子节点仍是右子节点),指向要删除节点的子节点便可。

3、这种状况最复杂,若是要删除有两个子节点的节点,就不能只用它的一个子节点代替它,好比要删除节点25,若是用35取代它,那35的左子节点是15呢仍是30?

                                                                                                       

所以须要考虑另外一种方法,寻找它的中序后继来代替该节点。下图显示的就是要删除节点用它的后继代替它的状况,删除后仍是有序的。(这里还有更麻烦的状况,即它的后继本身也有右子节点,下面再讨论。)

                                                                                                       

那么如何找后继节点呢?首先得找到要删除的节点的右子节点,它的关键字值必定比待删除节点的大。而后转到待删除节点右子节点的左子节点那里(若是有的话),而后到这个左子节点的左子节点,以此类推,顺着左子节点的路径一直向下找,这个路径上的最后一个左子节点就是待删除节点的后继。若是待删除节点的右子节点没有左子节点,那么这个右子节点自己就是后继。寻找后继的示意图以下:

                                                                                               

 

 找到了后继节点,如今开始删除了,先看第一种状况,后继节点是delNode右子节点的作后代,这种状况要执行如下四个步骤:

  • 把后继父节点的leftChild字段置为后继的右子节点;

  • 把后继的rightChild字段置为要删除节点的右子节点;

  • 把待删除节点从它父节点的leftChild或rightChild字段删除,把这个字段置为后继;

  • 把待删除的左子节点移除,将后继的leftChild字段置为待删除节点的左子节点。

以下图所示:

                                                                                         

若是后继节点就是待删除节点的右子节点,这种状况就简单了,由于只须要把后继为跟的子树移到删除的节点的位置便可。以下图所示:

                                                                                         

看到这里,就会发现删除时至关棘手的操做。实际上,由于它很是复杂,一些程序员都尝试着躲开它,他们在Node类中加了一个Boolean字段来标识该节点是否已经被删除,在其余操做以前会先判断这个节点是否是已经删除了,这样删除节点不会改变树的结构。固然树中还保留着这种已经删除的节点,对存储形成浪费,可是若是没有那么多删除的话,这也不失为一个好方法。

另外二叉树有三种遍历方式:前序、中序和后序。这个比较简单,直接看下代码便可。

下面手写个二叉搜索树的代码

 1  public class BinaryTree {
 2       private BNode root;   //根节点
 3      public BinaryTree() {  4         root = null;  5  }  6    
 7     //二叉搜索树查找的时间复杂度为O(logN)
 8     public BNode find(int key) { //find node with given key
 9       BNode current = root;  10       while(current.key != key) {  11         if (key < current.key) {  12              current = current.leftChild;  13          }else {  14              current = current.rightChild;  15  }  16          if(current == null) {  17              return null;  18  }  19  }  20      return current;  21  }  22 
 23    // 插入节点
 24    public void insert(int key, double value) {  25        BNode newNode = new BNode();  26        newNode.key = key;  27        newNode.data = value;  28        if(root == null) { //if tree is null
 29             root = newNode;  30  }  31         else {  32             BNode current = root;  33  BNode parent;  34             while(true) {  35                 parent = current;  36                 if(key < current.data) { //turn left
 37                     current = current.leftChild;  38                     if(current == null) {  39                         parent.leftChild = newNode;  40                         newNode.parent = parent;  41                         return;  42  }  43                 }else { //turn right
 44                     current = current.rightChild;  45                     if(current == null) {  46                         parent.rightChild = newNode;  47                         newNode.parent = parent;  48                         return;  49  }  50  }  51  }  52  }  53  }  54  
 55   //遍历二叉树    
 56   public void traverse(int traverseType) {  57      switch (traverseType){  58         case 1: System.out.println("Preorder traversal:");  59  preOrder(root);//前向遍历
 60                 break;  61         case 2: System.out.println("Inorder traversal:");  62  inOrder(root);//中向遍历
 63                 break;  64         case 3: System.out.println("Postorder traversal:");  65  postOrder(root);//后向遍历
 66                 break;  67         default: System.out.println("Inorder traversal:");  68  inOrder(root);  69                 break;  70  }  71         System.out.println("");  72  }  73 
 74     
 75   //前向遍历
 76   private void preOrder(BNode localRoot) {  77       if(localRoot != null) {  78            System.out.print(localRoot.data + " ");  79    preOrder(localRoot.leftChild);  80    preOrder(localRoot.rightChild);  81    }  82   }  83    
 84   //中向遍历
 85   private void inOrder(BNode localRoot) {  86      if(localRoot != null) {  87    inOrder(localRoot.leftChild);  88          System.out.print(localRoot.data + " ");  89    inOrder(localRoot.rightChild);  90    }  91   }  92 
 93     
 94   //后向遍历
 95   private void postOrder(BNode localRoot) {  96      if(localRoot != null) {  97    postOrder(localRoot.leftChild);  98    postOrder(localRoot.rightChild);  99           System.out.print(localRoot.data + " "); 100    } 101   } 102 
103   //查找最小值
104     
105   /*根据二叉搜索树的存储规则,最小值应该是左边那个没有子节点的那个节点*/
106     
107   public BNode minNumber() { 108      BNode current = root; 109      BNode parent = root; 110      while(current != null) { 111           parent = current; 112           current = current.leftChild; 113    } 114       return parent; 115   } 116    
117   //查找最大值
118     
119   /*根据二叉搜索树的存储规则,最大值应该是右边那个没有子节点的那个节点*/
120     
121   public BNode maxNumber() { 122         BNode current = root; 123         BNode parent = root; 124         while(current != null) { 125             parent = current; 126             current = current.rightChild; 127  } 128         return parent; 129  } 130 
131     
132   //删除节点
133     
134   /*
135  * 删除节点在二叉树中是最复杂的,主要有三种状况: 136  * 1. 该节点没有子节点(简单) 137  * 2. 该节点有一个子节点(还行) 138  * 3. 该节点有两个子节点(复杂) 139  * 删除节点的时间复杂度为O(logN) 140      */
141     
142   public boolean delete(int key) { 143         BNode current = root; 144 // BNode parent = root;
145         boolean isLeftChild = true; 146      if(current == null) { 147            return false; 148  } 149         //寻找要删除的节点
150         while(current.key != key) { 151 // parent = current;
152             if(key < current.key) { 153                 isLeftChild = true; 154                 current = current.leftChild; 155  } 156             else{ 157                 isLeftChild = false; 158                 current = current.rightChild; 159  } 160             if(current == null) { 161                 return false; 162  } 163  }
164         //找到了要删除的节点,下面开始删除 165         //1. 要删除的节点没有子节点,直接将其父节点的左子节点或者右子节点赋为null便可
166         if(current.leftChild == null && current.rightChild == null) { 167             return deleteNoChild(current, isLeftChild); 168  } 169      //3. 要删除的节点有两个子节点
170         else if(current.leftChild != null && current.rightChild != null) { 171             return deleteTwoChild(current, isLeftChild); 172  } 173 
174         //2. 要删除的节点有一个子节点,直接将其砍断,将其子节点与其父节点连起来便可,要考虑特殊状况就是删除根节点,由于根节点没有父节点
175         else{ 176             return deleteOneChild(current, isLeftChild); 177  } 178 
179  } 180 
181     
182   public boolean deleteNoChild(BNode node, boolean isLeftChild) { 183         if(node == root) { 184            root = null; 185             return true; 186  } 187         if(isLeftChild) { 188             node.parent.leftChild = null; 189  } 190         else{ 191             node.parent.rightChild = null; 192  } 193         return true; 194  } 195 
196     
197   public boolean deleteOneChild(BNode node, boolean isLeftChild) { 198         if(node.leftChild == null) { 199             if(node == root) { 200                 root = node.rightChild; 201                 node.parent = null; 202                 return true; 203  } 204             if(isLeftChild) { 205                 node.parent.leftChild  = node.rightChild; 206  } 207             else{ 208                 node.parent.rightChild = node.rightChild; 209  } 210             node.rightChild.parent = node.parent; 211  } 212         else{ 213             if(node == root) { 214                 root = node.leftChild; 215                 node.parent = null; 216                 return true; 217  } 218             if(isLeftChild) { 219                 node.parent.leftChild  = node.leftChild; 220  } 221             else{ 222                 node.parent.rightChild = node.leftChild; 223  } 224             node.leftChild.parent = node.parent; 225  } 226         return true; 227  } 228 
229     
230   public boolean deleteTwoChild(BNode node, boolean isLeftChild) { 231         BNode successor = getSuccessor(node); 232         if(node == root) { 233             successor.leftChild = root.leftChild; 234             successor.rightChild = root.rightChild; 235             successor.parent = null; 236             root = successor; 237  } 238         else if(isLeftChild) { 239             node.parent.leftChild = successor; 240  } 241         else{ 242             node.parent.rightChild = successor; 243  } 244         successor.leftChild = node.leftChild;//connect successor to node's left child
245         return true; 246  } 247 
248     //得到要删除节点的后继节点(中序遍历的下一个节点)
249     public BNode getSuccessor(BNode delNode) { 250         BNode successor = delNode; 251         BNode current = delNode.rightChild; 252         while(current != null) { 253             successor = current; 254             current = current.leftChild; 255  } 256         if(successor != delNode.rightChild) { 257             successor.parent.leftChild = successor.rightChild; 258             if(successor.rightChild != null) { 259                 successor.rightChild.parent = successor.parent;//删除后续节点在原来的位置
260  } 261             successor.rightChild = delNode.rightChild;//将后续节点放到正确位置,与右边连上
262  } 263         return successor; 264  } 265 } 266 
267 class BNode { 268     public int key; 269     public double data; 270     public BNode parent; 271     public BNode leftChild; 272     public BNode rightChild; 273 
274     public void displayNode() { 275         System.out.println("{" + key + ":" + data + "}"); 276  } 277 }         
相关文章
相关标签/搜索