二叉树构建、新增、删除和遍历总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/danielzhou888/article/details/78143607
敬请关注博客,后期不断更新优质博文,谢谢
源码:
------------------------------------------------------------------------------------
Node.java:
package cn.com.tree;
/**
 * 二叉树节点
 * @author daniel zhou
 *
 */
public class Node {
       // 数据项
       public long data ;
       // 数据项
       public String sData ;
       // 左子节点
       public Node leftChild ;
       // 右子节点
       public Node rightChild ;
      
       /**
       * 构造方法
       * @param data
       * @param sData
       */
       public Node( long data, String sData){
             this . data = data;
             this . sData = sData;
      }
}
--------------------------------------------------------------------------------
Tree.java:

package cn.com.tree;
/**
 * 二叉树类
 * @author daniel zhou
 *
 */
public class Tree {
      
       // 根节点
       public Node root ;
      
       /**
       * 原理:在一颗二叉树上插入节点,插入是建立在小于父节点,
       * 则插入到父节点左边,如果大于父节点,则插入到父节点右边。
       * 在插入树节点,需要判断树根是否为空,如果不为空,则需要
       * 当前节点指向树根和父节点指向当前节点。直到当前节点等于null,
       * 那么可以在父节点左边或者右边插入新节点,并且返回树根跳出循环。
       * 如果树根为空,直接把树根指向新创建的节点,实现过程如下所示:
       *
       * 插入节点
       * @param value
       */
       public void insert( long value, String sValue){
             // 封装节点
            Node newNode = new Node(value, sValue);
             // 引用当前节点
            Node current = root ;
             // 引用父节点
            Node parent;
             // 如果root为空,也就是第一次插入的时候
             if ( root == null ){
                   root = newNode;
                   return ;
            } else {
                   while ( true ){
                         // 父节点指向当前节点
                        parent = current;
                         // 如果当前指向的节点数据比插入的要大,则向左走
                         if (current. data > value){
                              current = current. leftChild ;
                               if (current == null ){
                                    parent. leftChild = newNode;
                                     return ;
                              }
                        } else {
                              current = current. rightChild ;
                               if (current == null ){
                                    parent. rightChild = newNode;
                                     return ;
                              }
                        }
                  }
            }
      }
      
       /**
       * 查找节点
       * @param value
       * @return
       */
       public Node find( long value){
             // 引用当前节点
            Node current = root ;
             // 循环,只要查找值不等于当前节点的数据项
             while (current. data != value){
                   // 进行比较,比较查找值和当前节点的大小
                   if (current. data > value){
                        current = current. leftChild ;
                  } else {
                        current = current. rightChild ;
                  }
                   // 如果查找不到
                   if (current == null ){
                         return null ;
                  }
            }
             return current;
      }
      

二叉树删除节点原理图:
由于过程比较复杂,这里用图来表示

       /**
       * 删除节点:
       * 工作原理:
                  从二叉查找树上删除节点的操作复杂程度取决于删除哪个节点。如果删除没有子节点的节点就非常简单,
                  如果节点只有一个子节点,不管是左子节点还是右子节点,就变得稍微有点复杂,如果节点包含两个子节点就最复杂。
                  如果待删除节点是叶子节点,那么只需要将从父节点指向它的链接指向null。
                  如果待删除节点只包含一个子节点,那么原本指向它的节点就得使其指向它的子节点。
                  如果待删除节点包含两个子节点,那么我们可以采用两种方式:
                  一种是查找待删除节点左子树上的最大值,
                  一种是查找待删除节点右节点上的最小值。
                  我们采取后者,找到最小值后,将临时节点上的值复制到待删除节点,然后再删除临时节点。
       * @param value
       */
       public boolean delete( long value){
             // 引用当前节点,从父节点开始
            Node current = root ;
             // 引用当前节点父节点
            Node parent = root ;
             // 是否为左节点
             boolean isLeftChild = true ;
            
             while (current. data != value){
                  parent = current;
                   // 进行比较
                   if (current. data > value){
                         // 向左走
                        current = current. leftChild ;
                        isLeftChild = true ;
                  } else {
                         // 向左走
                        current = current. rightChild ;
                        isLeftChild = false ;
                  }
                   // 如果查找不到
                   if (current == null ){
                         return false ;
                  }
            }
            
             // 找到对应的节点
             if (current. leftChild == null && current. rightChild == null ){
                   // 1,该节点没有子节点,直接删除叶子节点
                   // 如果是根节点
                   if (current == root ){
                         root = null ;
                  } else if (isLeftChild){
                         // 如果该节点是其父节点的左子节点,将其父节点的左子节点置为null
                        parent. leftChild = null ;
                  } else {
                         // 如果该节点是其父节点的右子节点,将其父节点的右子节点置为null
                        parent. rightChild = null ;
                  }
            } else if (current. rightChild == null ){
                   // 2,该节点没有右子节点,直接将该节点的左子节点挂在其父节点的左/右节点上
                   if (current == root ){
                         root = current. leftChild ;
                  } else if (isLeftChild){
                         // 如果该节点是其父节点的左子节点,将该节点的左子节点挂在其父节点的左子节点上
                        parent. leftChild = current. leftChild ;
                  } else {      
                         // 如果该节点是其父节点的右子节点,将该节点的左子节点挂在其父节点的右子节点上
                        parent. rightChild = current. leftChild ;
                  }
            } else if (current. leftChild == null ){
                   // 3,该节点没有左子节点,直接将该节点的右子节点挂在其父节点的左/右节点上
                   if (current == root ){
                         root = current. rightChild ;
                  } else if (isLeftChild){
                         // 如果该节点是其父节点的左子节点,将该节点的左子节点挂在其父节点的左子节点上
                        parent. leftChild = current. rightChild ;
                  } else {
                         // 如果该节点是其父节点的右子节点,将该节点的右子节点挂在其父节点的右子节点上
                        parent. rightChild = current. rightChild ;
                  }
            } else {
                   // 4,该节点有左右子节点
                   // 获取替代被删除节点的节点(即:找到该节点的中序后继节点,并将该节点替代被删除节点)
                  Node successor = getSuccessor(current);
                   if (current == root ){
                      root = successor;
                  } else if (isLeftChild){
                         // 如果该节点是其父节点的左子节点
                        parent. leftChild = successor;
                  } else {
                         // 如果该节点是其父节点的右子节点
                        parent. rightChild = successor;
                  }
                   // 固定替代节点的位置(固定其在被删除节点的位置,右边位置已经固定,现在固定左边)
                  successor. leftChild = current. leftChild ;
            }
            
             return true ;
      }
      
       /** 返回替代被删除节点的节点对象
       *  工作原理:
       *  被删除的有两个孩子节点,这种情况最复杂,因为要考虑到删除之后顺序不能乱。
       *    所以这种类型的节点要删除,如果直接删,真个树的大小顺序就乱了,所以需要考虑,
       *    在树中找到一个合适的节点来把这个节点给替换掉,用这种方法来保持整个数的稳定。
       *    所以又一个问题又来了了,该找哪个节点来替换它?结论是,需要在树中找出所有比
       *    被删除节点的值大的所有数,并在这些数中找出一个最小的数来。听起来很拗,如果
       *    把它用图形来描述的话,就是,从被删除的节点出发经过它的右节点,然后右节点最
       *    左边的叶子节点就是我们要找的,它有一个专业名词叫中序后继节点。下面专门来写一个方法来找它:
       */
       public Node getSuccessor(Node delNode) {
            
             // 替代节点
            Node successor = delNode;
             // 替代节点的父节点
            Node successorParent = delNode;
             // 从该节点的出发经过它的右节点
            Node current = delNode. rightChild ;
            
             //找到该节点右节点最左边的叶子节点(就是我们要找的替代该节点的节点)
             while (current != null ){
                  successorParent = successor;
                  successor = current;
                  current = current. leftChild ;
            }
            
             // 如果替代节点不为该节点的右节点
             if (successor != delNode. rightChild ){
                   // 替代节点的父节点的左节点=替代节点的右节点
                  successorParent. leftChild = successor. rightChild ;
                   // 替代节点(已经换到被删除节点的位置上)的右节点=原来被删除节点的右节点
                  successor. rightChild = delNode. rightChild ;
            }
            
             // 如果替代节点为该节点的右节点,直接返回
             return successor ;
      }
      
      
      
      
      
      
      
       //*************N--root,  L--left,  R--right****************//
      
       /** 遍历顺序:
  •              前序遍历:NLR
                      1.访问根节点
                      2.前序遍历左子树
                      3.前序遍历右子树
                  中序遍历: LNR
                      1.中序遍历左子树
                      2.访问根节点
                      3.中序遍历右子树
                  后序遍历: LRN
                      1.后序遍历左子树
                      2.后序遍历右子树
                      3.访问根节点
       */
      
      
      

   1 前序遍历
        
             遍历的顺序为:ABDGHCEIF
       /**
       * 基本思想:
                1.访问根节点
                2.前序遍历左子树
                3.前序遍历右子树
       * 顺序:NLR
       * 前序遍历(递归遍历)
       * @param localNode
       */
       public void frontOrder(Node localNode){
             if (localNode != null ){
                   // 访问根节点
                  System. out .println(localNode. data + ", " + localNode. sData );
                   // 前序遍历左子树
                  frontOrder(localNode. leftChild );
                   // 前序遍历右子树
                  frontOrder(localNode. rightChild );
            }
      }
      
       /**
       * 前序非递归遍历:
                  对于任一结点p:
              a. 访问结点p,并将结点p入栈;
              b. 判断结点p的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,
                            并将栈顶结点的右孩子置为当前的结点p,循环置a;若不为空,则将p的左孩子置为当前结点p;
              c. 直到p为空,并且栈为空,则遍历结束。
       * @param localNode
       */
       public void frontOrder2(Node localNode){
            
      }
      


    2 中序遍历
    
           遍历的顺序为:GDHBAEICF
       /**
       * 基本思想:
                1.中序遍历左子树
                2.访问根节点
                3.中序遍历右子树
       * 中序遍历
       * 顺序:LNR
       * @param localNode
       */
       public void inOrder(Node localNode){
             if (localNode != null ){
                   // 中序遍历左子树
                  inOrder(localNode. leftChild );
                   // 访问根节点
                  System. out .println(localNode. data + ", " +localNode. sData );
                   // 中序遍历右子树
                  inOrder(localNode. rightChild );
                  
            }
      }
      



    3 后序遍历
               遍历的顺序为:GHDBIEFCA
       /**
       * 基本思想:
                1.后序遍历左子树
                2.后序遍历右子树
                3.访问根节点
       * 后序遍历
       * 顺序:LRN
       * @param localNode
       */
       public void afterOrder(Node localNode){
             if (localNode != null ){
                   // 后序遍历左子树
                  afterOrder(localNode. leftChild );
                   // 后序遍历右子树
                  afterOrder(localNode. rightChild );
                   // 访问根节点
                  System. out .println(localNode. data + ", " +localNode. sData );
            }
      }
      
      
       4 层序遍历
    规则是若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问, 从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。
 
遍历的顺序为:ABCDEFGHI





      
      
      
}
--------------------------------------------------------------------------------

猜你喜欢

转载自blog.csdn.net/danielzhou888/article/details/78143607