第八章 二叉树

原创 2015年11月21日 19:35:28
一、综述
    
     为什么要用树,这种新的数据结构呢?因为树结合了有序数组和链表的优势——在树中查找数据项的速度和有序数组一样快,并且在插入和删除数据项和链表一样快。
     树由边连接的节点组成,如下图:

     节点一般代表着现实生活中的实体,在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)。 

python核心编程(第二版)参考答案(自制)--第八章·条件和循环

###最近自学python,使用的教材是python核心编程(第二版)。自己做了练习题的答案,不管正确与否,算是给自己的一种约束和督促吧。 ---------------------------...
  • zflzfl1023
  • zflzfl1023
  • 2014年08月17日 17:00
  • 1301

第一行代码笔记,第八章-----多媒体的使用

1:通知的使用:       通知:比如:头条,淘宝,各种APP打开时,发送的提示信息,(智能手机从最上面拉下来的界面就是通知界面显示的地方,也就是所谓的状态栏)。        1.1 通知的基...
  • TAR1314520
  • TAR1314520
  • 2016年10月13日 16:00
  • 228

《统计学习方法,李航》:8、提升方法Boosting(1)

1)Boosting思想和基本概念 2)AdaBoost算法 3)AdaBoost算法举例 1)Boosting思想和基本概念   下面的概念前面都讲过: PAC(probably ap...
  • mmc2015
  • mmc2015
  • 2015年01月23日 12:55
  • 1445

c++primer plus 第八章习题答案(自己写的)

#include #include //第八章 using namespace std; void showstr(char *str, int n = 0); int main() { char ...
  • chinahnwqf
  • chinahnwqf
  • 2016年05月11日 23:25
  • 681

Java编程思想第四版读书笔记——第八章 多态

本来不打算看了,现在面试笔试发现还是要重新拾起来看一遍。老老实实啃砖吧  第八章 多态 在面向底下的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。 “封装”通过合并特征和行为来创建新...
  • severusyue
  • severusyue
  • 2016年06月20日 22:23
  • 710

<Python核心编程(第二版)>第八章练习题

1. 条件语句. 请看下边代码:  # statement A  if x > 0:      # statement B      pass  elif x 0:      # stat...
  • qq_20113327
  • qq_20113327
  • 2017年03月10日 18:02
  • 248

C++ Primer Plus第六版 第八章 编程练习答案

23333 时隔一年我来填坑了 CPP继续往后读然后把题目写了_(:з」∠)_ 写到哪更到哪 不一次性更完了...
  • zhaoshu01234
  • zhaoshu01234
  • 2015年08月06日 00:01
  • 2105

Java编程思想第四版第8章练习

public class MainTest { public static void main(String args[]){ Cycle[] cycle=...
  • zhaoqingkaitt
  • zhaoqingkaitt
  • 2014年11月18日 10:03
  • 1169

C Primer Plus(第6版)第八章答案

1.#include int main (void) { int ch;//字符 int i = 0;//字符数 while ((ch = getchar ()) != ...
  • sirius_black_tea
  • sirius_black_tea
  • 2017年07月21日 15:00
  • 395

Java编程思想(第八章)

多态是面向对象设计语言的三种基本特征之一。 1、把对象的引用视为对其基类型的对象引用的做法称为上转型。类A1、A2、A3都是基类A的子类,类B需要一个方法分别传入A1、A2、A3,如果我们分别使用A...
  • fulunyong
  • fulunyong
  • 2016年10月26日 22:37
  • 260
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:第八章 二叉树
举报原因:
原因补充:

(最多只允许输入30个字)