zoukankan      html  css  js  c++  java
  • 创建二叉树

    创建二叉树

    2011611

    21:28

    有以下4
    1:树本身有一个根节点root

    2:静态内部类Node,其中有左子节点,右子节点,以及构造方法。

    3:还有insert方法,将节点插入到数的root

    4:最后是一个构建树的方法

    5:再往下就可以是对节点的操作方法了。

    public class BinaryTree {
    private Node root;
     
    /*定义一个静态内部类,Node节点*/
    public static class Node {
    Node left;
    Node right;
    int data;
     
    Node(int newData) {
    left = null;
    right = null;
    data = newData;
    }
    }
     
    /*二叉树的构造方法*/
    public BinaryTree() {
    root = null;
    }
     
    /*insert方法*/
    public void insert(int data) {
     
    root = insert(root, data);
    }
     
    public Node insert(Node node, int data) {
    if(node==null) {
    node = new Node(data);
    } else {
    if(data<= node.data) {
    node.left = insert(node.left, data);//递归这里的insert,才形成 的树,其实是二叉排序树。
    } else {
    node.right = insert(node.right, data);
    }          
    }
    return node;
     
    }
     
    /*构建二叉树*/
    public void buildTree(int[] a){
    for(int data:a){
    insert(data);
    }
    }
     
    /*//下面就是中序遍历啊*/        
    public void printTree() {
    printTree(root);
    /*System.out.println();*/
    }
     
     
    public void printTree(Node node) {
    if(node==null)return;
    printTree(node.left);
    System.out.println(node.data+"");
    printTree(node.right);
    }
     
     
     
    }


     二 递归构建二叉树

    今天顶着要交年终文档的压力,总算理清了递归方式实现二叉树的方法,彻底弄明白要如何建一颗二叉树了!终于了切了一桩心愿!苦苦思索近两个星期了,郁闷得快不行了。终于一见庐山真面目,真是凡事一定要坚持到底呀!

           言归正传,下面讲如何创建二叉树,以及遍历二叉树。

    一、 首先记录一下自己一直迷惑不解的几个问题

    1.1    一颗二叉树是如何表示的呢?为什么 yanghui 说返回一个根 root 结点就行了呢?

    1.2    二叉树结点的结构应该如何设置?以前都是 {int data; Node left;Node right;} 就行了,为什么 yanghui ,chexiaoyao 他们建的树是第一个孩子和第一个弟弟的结构,有层次( int level )等项呢?

    1.3    递归怎么写?递归实现一颗树又怎么写?

    1.4    创建一个树的函数( buildTree(),insert(),create() )应该怎么写?应该接收什么参数,返回什么?为什么yanghui 说肯定要传一个 root 参数,并且返回一个根 root 结点呢?不传根 root 参数可以吗?返回的结点好像是每次加入的新结点呀,怎么直接 return root ,就是根 root 结点了呢?真是想不明白啊。一个研二的学生了,也不好意思去问别人,人家都是考研过来的,这些早就熟烂了。

    1.5    根据网上的建议,看了递归的内容,重新了解了堆栈的概念,看了自己买的《数据结构 算法与应用 C++ 》二叉树部分,递归方法应该是掌握了,这本书中罗列了三种递归的遍历方式,清晰明了。但是偏偏我最想要的内容建立二叉树的函数,这本书里没有,有的只是根据已有的左右子树合并成一颗新树,晕,这点谁不会!郁闷啊,难怪自己一直没有理解如何建立二叉树,一则是完成 wuping 老师的程序的时候,照着她的书拷贝程序,程序调通了就万事 ok ,哪里还管其他的,二则自己的救命草就是这本书,这本书没有,自己肯定也没去思考过,更没有去找过其他的资料。

     

    二、 在编码实践中领悟和解决上述问题

    在 baidu 和 google 上搜索了众多的资料,但是搜索结果很令人失望,没讲什么内容,而且程序还不能确保正确。大部分也都是 c 或者 c++ 写的, java 的程序很难搜索到。看了一两天这些网页上的程序,感觉思路越来越乱了!

    某一天上午,突然灵光一动,上次自己搜索到的决策树的好东西都是在 google 中用全英文的关键字搜索到的,以前看过一个帖子提过 google 中如果用英文检索,会得到很多好结果。于是,在 google 中输入“ Binary Trees ”,哇,第一条的内容是斯坦福大学 CS 专业的二叉树的代码和讲解,并且有 C,C++,JAVA 三个版本的代码,以及详细的讲解,这正是我梦寐以求的资料!我知道我将有希望搞定二叉树了!

    2.1 BinaryTree 类的代码如下:

    http://cslibrary.stanford.edu/110/BinaryTrees.html

    // BinaryTree.java

    package study;

     

    public class BinaryTree {

      // Root node pointer. Will be null for an empty tree.

      private Node root ;

     

      private static class Node {

        Node left ;

        Node right ;

        int data ;

     

        Node( int newData) {

          left = null ;

          right = null ;

          data = newData;

        }

      }

     

      /**

       Creates an empty binary tree -- a null root pointer.

      */

      public BinaryTree() {

        root = null ;

      }

     

      /**

       Inserts the given data into the binary tree.

       Uses a recursive helper.

      */

      public void insert( int data) {

        root = insert( root , data);

      }

     

     

      /**

       Recursive insert -- given a node pointer, recur down and

       insert the given data into the tree. Returns the new

       node pointer (the standard way to communicate

       a changed pointer back to the caller).

      */

      private Node insert(Node node, int data) {

        if (node== null ) {

          node = new Node(data);

        }

        else {

          if (data <= node. data ) {

            node. left = insert(node. left , data);

          }

          else {

            node. right = insert(node. right , data);

          }

        }

     

        return (node); // in any case, return the new pointer to the caller

      }

     

       

      public void buildTree( int [] data){

         

          for int i=0;i<data. length ;i++){

             insert(data[i]);

          } 

      }

     

      public void printTree() {

          printTree( root );

          System. out .println();

         }

      private void printTree(Node node) {

          if (node == null return ;

     

          // left, node itself, right

          printTree(node. left );

          System. out .print(node. data + "  " );

          printTree(node. right );

         }

     

     

    }

     

     

     

    测试类代码如下:

    //test.java

    public class test {

        public static void main(String[] args) {

           BinaryTree biTree=new BinaryTree();

           int[] data={2,8,7,4};

           biTree.buildTree(data);

           biTree.printTree();

          

        }

    }

     

     

    2.2  Node 类

    private static class Node {

        Node left ;

        Node right ;

        int data ;

     

        Node( int newData) {

          left = null ;

          right = null ;

          data = newData;

        }

      }

     

    2.2.1 注意它的封装性和访问控制权限。设置为 private 类以及 BinaryTree 的函数都相应有 private 和 public 方法来实现很好的封装和访问控制,这种方式以后自己要多加学习,积极运用。

     

    2.2.2 把类设置为 static 的作用和艺术??自己还没有理解,也还没有查资料,有待学习。。。不用 static 关键字程序也能运行。 Static 类有何用途呢??

     

     

    2.3 关键函数 insert()

    private Node insert(Node node, int data) {

        if (node== null ) {

          node = new Node(data);

        }

        else {

          if (data <= node. data ) {

            node. left = insert(node. left , data);

          }

          else {

            node. right = insert(node. right , data);

          }

        }

     

        return (node); // in any case, return the new pointer to the caller

      }

     

     

    自己对这个 insert() 研究了两三天,今天总算琢磨明白了!

     

    2.3.1 为什么需要 Node node 参数?能不要这个参数吗?也就是这个函数如果是 insert(int data) 可以实现创建二叉树的功能吗?

    编码实践实验:

    1 )把 Node node 参数去掉,写一个函数 insert(int data) 如下:

    private Node insert( int data) {

        if (node== null ) {

          root = new Node(data);

        }

        else {

          if (data <= root. data ) {

            root. left = insert(data);

          }

          else {

            root. right = insert(data);

          }

        }

     

        return (node); // in any case, return the new pointer to the caller

      }

     

     

    这时,程序运行会报一大堆的 stack 溢出错误。刚开始怎么也想不明白为什么会 stack 溢出。后来,自己手工用一个例子数据 {2 , 1 , 3 , 6 , 4} 一步一步按照上述代码走一遍,终于发现错误根源!当运行到 data = 1 的时候,程序便在这里 ” root. left = insert(data); ” 进入死循环了!!因为没有退出递归的条件!!

     

    发现问题后,思索了一会,发现其实还是自己根本没有理解递归!这个程序是递归算法,递归算法的本质是要有一个不断递归的变量,比如说求 n !阶层的递归函数中,要传一个 n 变量,并且 n 变量要每次调用都自减 1 。写一个递归函数就要先清楚要根据哪一个自变化的递归变量递归呀!!上述 insert(int data) 函数没有了递归变量 Node node ,当然就进入死循环, stack 溢出了!

    所以,如果要用递归的方式实现创建二叉树的函数,那么这个根 root 结点参数肯定是要有的!

    现在确定, insert 函数必须有两个参数。下面接着讨论返回值的问题。

     

    2.3.2 为什么需要返回值?返回值为 void 可以吗? C ++版本的建树函数是可以不用返回值的。

    1 )把返回值去掉,写一个函数 void insert(Node node , int data) 如下:

    private void insert(Node node, int data) {

        if (node== null ) {

          node = new Node(data);

        }

        else {

          if (data <= node. data ) {

                insert(node. left , data);

          }

          else {

                 insert(node. right , data);

          }

        }

     

      }

     

     

           这时程序运行,没有任何的输出结果。百思不得其解呀!后来考虑到 C++ 中是传引用或者指针,而 java 中是值传递的问题,猜到可能是没有返回值的话, root 数据成员可能始终为空,因为函数的调用丝毫不能影响实参。于是加上root 的返回值,代码如下:

    private Node insert(Node node, int data) {

        if (node== null ) {

          node = new Node(data);

        }

        else {

          if (data <= node. data ) {

                insert(node. left , data);

          }

          else {

                 insert(node. right , data);

          }

        }

         return node ;

      }

     

    此时,插入数据 int data[]={2,1,3,6,4}, 程序有输出了,但是只输出“ 2 ”,也就是只能输出 root 根结点的值。于是,把 root.left 和 root.right 的引用加上,因为猜想可能还是因为 java 是值传递的问题,使得 root.left 和root.right 始终为空。加上后代码如下:

    private Node insert(Node node, int data) {

        if (node== null ) {

          node = new Node(data);

        }

        else {

          if (data <= node. data ) {

            node. left = insert(node. left , data);

          }

          else {

            node. right = insert(node. right , data);

          }

        }

     

        return (node); // in any case, return the new pointer to the caller

      }

     

    哈,验证了自己的猜想,此时能够正确输出结果了:(中序遍历)

    1  2  3  4  6

    ok !

     

    三、 小结

    已经是 20070202 01 : 44 了,通过今晚,二叉树的困惑已经没太大问题了!这两个星期时常对它们的苦苦思索也将告终,虽然有点累了,也担心明早起不来,而且还要交文档,但心情还是挺愉快的。总算没有辜负先放弃文档的代价。

    二叉树对别人来说可能是微不足道的程序,但这次对于我却至关重要,关键是信心的问题,现在又对自己充满信心了!不怕乌龟跑得慢,就怕它没能坚持一直往前冲!

     

    最后小结一下二叉树的知识点:

    1)  理解递归方法,理解要又一个自变化的递归变量作参数;

    2)  理解编译原理的 stack 的概念;

    3)  理解引用调用,值调用的方式;

    4)  理解 root 根结点的引用;创建一颗树时,要返回一个 root 根结点的应用,这样才能根据 root 找到这颗树!

    5)  进一步加深了对写一个函数关键要确定接口,即参数和返回值;

    6)  理解上述 private Node insert(Node node, int data) 函数为什么每次调用得到都是 root 根结点,而不是新加入的新结点。这一点很重要。自己可以手工一步一步把代码走一遍就明白了!

    7)  这次调程序的一个经验和收获:

    就像 yanghui 说的,写程序之前,自己也手工算一遍并走一遍!这点太重要了,尤其时算法类的程序!这次能够最终理解二叉树,就是靠手工一步一步走一遍,最终发现问题所在的!

     

    Ok !

    三 斯坦福二叉树构建与遍历

    如果用Google搜索技术类问题,没有比较好的文章,那么用英文,所以这里输入 binary trees就得到了斯坦福大学用c/c++ , Java三种语言解答的这个问题:http://cslibrary.stanford.edu/110/BinaryTrees.html

     

    // BinaryTree.java

    package Tree;

     

    public class BinaryTree {

      // Root node pointer. Will be null for an empty tree.

      private Node root ;

     

      private static class Node {

        Node left ;

        Node right ;

        int data ;

     

        Node( int newData) {

          left = null ;

          right = null ;

          data = newData;

        }

      }

     

      /**

       Creates an empty binary tree -- a null root pointer.

      */

      public BinaryTree() {

        root = null ;

      }

     

      /**

       Inserts the given data into the binary tree.

       Uses a recursive helper.

      */

      public void insert( int data) {

        root = insert( root , data);

      }

     

     

      /**

       Recursive insert -- given a node pointer, recur down and

       insert the given data into the tree. Returns the new

       node pointer (the standard way to communicate

       a changed pointer back to the caller).

      */

      private Node insert(Node node, int data) {

        if (node== null ) {

          node = new Node(data);

        }

        else {

          if (data <= node. data ) {

            node. left = insert(node. left , data);

          }

          else {

            node. right = insert(node. right , data);

          }

        }

     

        return (node); // in any case, return the new pointer to the caller

      }

     

       

      public void buildTree( int [] data){

         

          for int i=0;i<data. length ;i++){

             insert(data[i]);

          } 

      }

     

      public void printTree() {

          printTree( root );

          System. out .println();

         }

      private void printTree(Node node) {

          if (node == null return ;

     

          // left, node itself, right

          printTree(node. left );

          System. out .print(node. data + "  " );

          printTree(node. right );

         }

     

     

    }

     

     

     

    测试类代码如下:

    //test.java

    public class test {

        public static void main(String[] args) {

           BinaryTree biTree=new BinaryTree();

           int[] data={2,8,7,4};

           biTree.buildTree(data);

           biTree.printTree();

          

        }

    }


     

  • 相关阅读:
    mac 显示隐藏文件 快捷键
    Swift常用内置函数介绍
    MacOS Catalina 虚拟机VMware Fusion 重装教程 和问题
    swift 时间显示格式
    mybatis自测错题总结
    Spring核心概念和案例
    MyBatis注解
    MyBatis缓存
    关联查询
    智能标签
  • 原文地址:https://www.cnblogs.com/allenzhaox/p/3201783.html
Copyright © 2011-2022 走看看