关闭

第八章 二叉树

标签: 二叉树
99人阅读 评论(0) 收藏 举报
分类:
一、综述
    
     为什么要用树,这种新的数据结构呢?因为树结合了有序数组和链表的优势——在树中查找数据项的速度和有序数组一样快,并且在插入和删除数据项和链表一样快。
     树由边连接的节点组成,如下图:

     节点一般代表着现实生活中的实体,在Java中也称对象。节点间的边表示关联节点间的路径,在Java中用引用来表示边。在树的顶层总是只有一个节点,称为根。它通过边连接到第二层的多个节点,而第二层的节点连接到第三层的多个节点,以此类推。树有很多中,如果每个节点最多只有两个字节点,那么这种树称为二叉树。

     根:树顶端的节点
     父节点:从某个节点(除了根)都恰好沿着一个边向上连接到另一个节点。
     子节点:每个节点都有一条或多条边向下连接到另一个节点。
     叶节点:没有子节点的节点。
     子树:每个节点都可以作为“子树”的根。
     访问:当程序控制流程到达某个节点,称为访问。
     遍历:遵循某种特定的顺序访问树中的所有节点。
     层:一个节点的层树表示从根开始到这个节点有多少“代”。
     关键值:对象中指定某个数据域为关键值。

二、用Java代码表示树

1、Node类,用来表示节点对象。包含数据。
class Node
{
     int iData;
     Node leftChild;
     Node rightChild;
}

2、Tree类,用来表示树对象。含有所有对象,左节点的关键值总小于右节点的关键值。
class Tree
{
     private Node root;

     //根据给定的关键值找到节点
     public Node find(int key)
     {
          Node current = root;
          while(current.iData != key) 
          {
               if(key < current.iData)
                    current = current.leftChild;
               else
                    current = current.rightChild;
               
               if(current == null)
                    return null;
          }
          return current;
     }

     //按照左边字节点小于右边子节点的规则找到新节点的位置。
     public void insert(int iData)
     {
          Node newNode = new Node();
          newNode.iData = iData;
          
          if(root == null)
               root = newNode;
          else
          {
               Node current = root;
               Node parent;
               while(true)
               {
                    parent = current;
                    if(iData < current.iData)
                    {
                         current = current.leftChild;
                         if(current == null)
                         {
                              parent.leftChild = newNode;
                              return;
                         }
                    }
                    else
                    {
                         current = current.rightChild;
                         if(current == null)
                         {
                              parent.rightChild = newNode;
                              return;   
                         }
                    }
               }
          }
     }

     public void delete(int iData)
     {
     }
}

     树的查找效率:查找节点的时间取决于这个节点所在的层数。假设有31个节点,不超过5层,那么只需要最多5次比较,就可以找到任何节点。因此它的时间复杂度是O(logN)。

3、遍历树
     三种方法:前序、中序、后序。遍历树的最简单方法是使用递归。

3.1中序遍历
     中序是先遍历节点的左子树、再访问节点、再遍历节点的右子树。中序遍历会使所有的节点按关键字升序被访问到。
     private void inOrder(Node localRoot)
     {
          if(localRoot != null)
          {
               inOrder(localRoot.leftChild);
               System.out.print(localRoot.iData+””);
               inOrder(localRoot.rightChild);
          }
     }

3.2前序遍历
     前序是先访问节点,再遍历节点的左子树,再遍历节点的右子树。

3.3后序遍历
     后序是先遍历节点的左子树,再遍历节点的右子树,最后访问节点。

3.4查找最大值和最小值
     在二叉树中找到最大值和最小值非常简单。比如要找最小值,先走到根的左子节点,然后接着走到那个字节点的左子节点,依次类推,直到找到一个没有左子节点的节点,这个节点就是最小值的节点。同理,找最大值就是找右子节点。

4、删除节点
     需要分三类情况:
  1. 该节点是叶节点
  2. 该节点有一个子节点
  3. 该节点有两个子节点

4.1删除叶节点
     只需要改变该节点的父节点的对应子字段的值,由指向该节点改为null即可,等待GC回收该节点。

     public boolean delete(int key)
     {
          Node current = root;
          Node parent = root;
          boolean isLeftChild = true;

          while(current.iData != key)
          {
               parent = current;
               if(key < current.iData)
               {
                    isLeftChild = true;
                    current = current.leftChild;
               }
               else
               {
                    isLeftChild = false;
                    current = current.rightChild;
               }
               if(current == null)
                    return false;
          }
          
          if(current.leftChild == null && current.rightChild == null)
          {
               if(current == root)
                    root = null;
               else if(isLeftChild)
                    parent.leftChild = null;
               else
                    parent.rightChild = null;
          }
     }

4.2删除有一个子节点的节点
     将待删除的子节点删除,它的子节点直接连接到它的父节点上。
else if(current.rightChild == null)
     if(current == root)
          root = current.leftChild;
     else if(isLeftChild)
          parent.leftChild = current.leftChild;
     else
          parent.rightChild = current.leftChild;

else if(current.leftChild == null)
     if(current == root)
          root = current.rightChild;
     else if(isLeftChild)
          parent.leftChild = current.rightChild;
     else 
          parent.rightChild = current.rightChild;

4.3删除有两个子节点的节点
     删除有两个子节点的节点,用它的中序后继(找到比初始节点关键值大的节点集合中最小的节点)来代替该节点。

//找后继节点的代码块
private Node getSuccessor(Node delNode)
{
     Node successorParent = delNode;
     Node successor = 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;
}

     找到后继节点后就是处理删除了,这边也有两种情况:
     1、后继节点就是delNode的右子节点。
     if(current == root)
          root = successor;
     else if(isLeftChild)
          parent.leftChild = successor;
     else 
          parent.rightChild = successor;

     successor.leftChild = current.leftChild;

     2、后继节点是delNode右子节点的左后代
     第一步:把后继父节点的leftChild字段置为successor的右子节点。
     第二步:把successor的rightChild字段置为要删除节点的右子节点。
     第三步:把current从父节点的rightChild字段移除,把这个字段置为successor。
     第四步:把current的左子节点从current移除,successor的left字段置为current的左子节点。

5、二叉树的效率
     树的大部分操作都需要从上到下一层一层地查找某个节点,一颗满树中,大约一半的节点在最底层。因此算起来二叉树的效率是O(logN)。 
0
0

猜你在找
【直播】机器学习&数据挖掘7周实训--韦玮
【套餐】系统集成项目管理工程师顺利通关--徐朋
【直播】3小时掌握Docker最佳实战-徐西宁
【套餐】机器学习系列套餐(算法+实战)--唐宇迪
【直播】计算机视觉原理及实战--屈教授
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之矩阵--黄博士
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之凸优化--马博士
【套餐】Javascript 设计模式实战--曾亮
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:6336次
    • 积分:324
    • 等级:
    • 排名:千里之外
    • 原创:25篇
    • 转载:0篇
    • 译文:2篇
    • 评论:0条
    文章存档