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

来源:互联网 发布:淘宝账户暂时被冻结 编辑:程序博客网 时间:2024/06/06 15:38
源码:
------------------------------------------------------------------------------------
Node.java:
packagecn.com.tree;
/**
 * 二叉树节点
 *@author daniel zhou
 *
 */
publicclass Node {
      // 数据项
      publiclong data;
      // 数据项
      publicStringsData;
      // 左子节点
      publicNodeleftChild;
      // 右子节点
      publicNoderightChild;
      
      /**
       * 构造方法
       *@param data
       *@param sData
       */
      publicNode(longdata, String sData){
            this.data= data;
            this.sData= sData;
      }
}
--------------------------------------------------------------------------------
Tree.java:

packagecn.com.tree;
/**
 * 二叉树类
 *@author daniel zhou
 *
 */
publicclass Tree {
      
      // 根节点
      publicNoderoot;
      
      /**
       * 原理:在一颗二叉树上插入节点,插入是建立在小于父节点,
       * 则插入到父节点左边,如果大于父节点,则插入到父节点右边。
       * 在插入树节点,需要判断树根是否为空,如果不为空,则需要
       * 当前节点指向树根和父节点指向当前节点。直到当前节点等于null,
       * 那么可以在父节点左边或者右边插入新节点,并且返回树根跳出循环。
       * 如果树根为空,直接把树根指向新创建的节点,实现过程如下所示:
       *
       * 插入节点
       *@param value
       */
      publicvoid insert(longvalue, 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
       */
      publicNode find(longvalue){
            // 引用当前节点
            Node current =root;
            // 循环,只要查找值不等于当前节点的数据项
            while(current.data!= value){
                  // 进行比较,比较查找值和当前节点的大小
                  if(current.data> value){
                        current = current.leftChild;
                  }else{
                        current = current.rightChild;
                  }
                  // 如果查找不到
                  if(current == null){
                        returnnull;
                  }
            }
            returncurrent;
      }
      

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

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


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





      
      
      
}
--------------------------------------------------------------------------------
原创粉丝点击