zoukankan      html  css  js  c++  java
  • JAVA二叉树的实现

    一、Node节点的java实现
    首先在可以看到打上Node这个字符串,就可以看到只能的IDEA系统提供的好多提示:
    
    
    点进去看,却不是可以直接构成二叉树的Node,不是我们需要的东西。这里举个例子来看
    org.w3c.dom
    这里面的Node是一个接口,是解析XML时的文档树。在官方文档里面看出:该 Node 接口是整个文档对象模型的主要数据类型。它表示该文档树中的单个节点。当实现 Node 接口的所有对象公开处理子节点的方法时,不是实现 Node 接口的所有对象都有子节点。
    
    所以我们需要自定义一个Node类
    
    package com.sort.text;
    
    public class Node {
        private int value;        //节点的值
        private Node node;        //此节点,数据类型为Node
        private Node left;        //此节点的左子节点,数据类型为Node
        private Node right;       //此节点的右子节点,数据类型为Node
        
        public int getValue() {
            return value;
        }
    
        public void setValue(int value) {
            this.value = value;
        }
    
        public Node getNode() {
            return node;
        }
    
        public void setNode(Node node) {
            this.node = node;
        }
    
        public Node getLeft() {
            return left;
        }
    
        public void setLeft(Node left) {
            this.left = left;
        }
    
        public Node getRight() {
            return right;
        }
    
        public void setRight(Node right) {
            this.right = right;
        }
    
        public Node(int value) {
            this.value=value;
            this.left=null;
            this.right=null;
        }
        public String toString() {         //自定义的toString方法,为了方便之后的输出
            return this.value+" ";
        }
    }
    
    定义好了之后就可以开始直接使用了,相信大家都可以秒看懂。
    
    二、数组升华二叉树
    一般拿到的数据是一个int型的数组,那怎么将这个数组变成我们可以直接操作的树结构呢?
    
    1、数组元素变Node类型节点
    2、给N/2-1个节点设置子节点
    3、给最后一个节点设置子节点【有可能只有左节点】
    
    那现在就直接上代码
    
    public  static void create(int[] datas,List<Node> list) {
    
            //将数组里面的东西变成节点的形式
            for(int i=0;i<datas.length;i++) {
                Node node=new Node(datas[i]);
                list.add(node);
            }
            
            //节点关联成树
            for(int index=0;index<list.size()/2-1;index++) {
                list.get(index).setLeft(list.get(index*2+1));
                              //编号为n的节点他的左子节点编号为2*n 右子节点编号为2*n+1 但是因为list从0开始编号,所以还要+1
                list.get(index).setRight(list.get(index*2+2));  //与上同理
            }
            
            //单独处理最后一个父节点 ,list.size()/2-1进行设置,避免单孩子情况
            int index=list.size()/2-1;
            list.get(index).setLeft(list.get(index*2+1));
            if(list.size()%2==1)     
                     //如果有奇数个节点,最后一个父节点才有右子节点
                list.get(index).setRight(list.get(index*2+2));
                
        }
    
    很细致的加上了很多的注释啊,所以保证一看就懂。
    开始大招前的攒金币过程正式结束
    现在开始放大招
    
    三、递归前序遍历
    具体的原理没有什么好讲的,知道顺序即可
    
    先序遍历过程:
    (1)访问根节点;
    (2)采用先序递归遍历左子树;
    (3)采用先序递归遍历右子树;
    
    这里用图来说明
    
    先序遍历结果:A BDFE CGHI
    
    还是看代码吧
    
     public void preTraversal(Node node){
           if (node == null) //很重要,必须加上 当遇到叶子节点用来停止向下遍历
                 return; 
             System.out.print(node.getValue()+" ");
            preTraversal(node.getLeft());
            preTraversal(node.getRight());
     }
    
    看,说了很简单吧!
    
    四、递归中序遍历
    中序遍历:
    (1)采用中序遍历左子树;
    (2)访问根节点;
    (3)采用中序遍历右子树
    
    
    中序遍历结果:DBEF A GHCI
    
    有请代码:
    
    public void    MidTraversal(Node node){
          if (node == null)
              return; 
         MidTraversa(node.getLeft());
         System.out.print(node.getValue()+" ");
         MidTraversa(node.getRight());
     }
    
    五、递归后序遍历
    后序遍历:
    (1)采用后序递归遍历左子树;
    (2)采用后序递归遍历右子树;
    (3)访问根节点;
    
    
    
    后序遍历的结果:DEFB HGIC A
    
    代码:
    
     public void postTraversal(Node node){
             if (node == null) 
                     return; 
             postTraversal(node.getLeft());
            postTraversal(node.getRight());
            System.out.print(node.getValue()+" ");
     }
    
    其实代码和思想一样,只是输出的位置和递归调用的位置不同而已。
    
    个人觉得懂得非递归的原理和代码比懂递归更有意思,当你能手撕非递归二叉树遍历的时候,面试官问你原理,还能不知道吗?
    
    那接下来的三个模块就是非递归的三种遍历
    
    拭目以待
    
    六、非递归前序遍历
    我这里使用了栈这个数据结构,用来保存不到遍历过但是没有遍历完全的父节点
    之后再进行回滚。
    
    基本的原理就是当循环中的p不为空时,就读取p的值,并不断更新p为其左子节点,即不断读取左子节点,直到一个枝节到达最后的子节点,再继续返回上一层进行取值
    
    代码:
    
    public void preOrderTraversalbyLoop(Node node){
              Stack<Node> stack = new Stack();
              Node p = node;
              while(p!=null || !stack.isEmpty()){
                     while(p!=null){ 
                     //当p不为空时,就读取p的值,并不断更新p为其左子节点,即不断读取左子节点
                       System.out.print(p.getValue()+" ");
                       stack.push(p); //将p入栈
                       p = p.getLeft(); 
                   }
                  if(!stack.isEmpty()){
                   p = stack.pop();
                   p = p.getRight();
                 }
            }
     }
    
    执行结果,很顺利的得到想要的结果
    
    
    
    七、非递归中序遍历
    同原理
    
    就是当循环中的p不为空时,就读取p的值,并不断更新p为其左子节点,但是切记这个时候不能进行输出,必须不断读取左子节点,直到一个枝节到达最后的子节点,然后每次从栈中拿出一个元素,就进行输出,再继续返回上一层进行取值
    
    代码如下:
    
    public void inOrderTraversalbyLoop(Node node){
             Stack<Node> stack = new Stack();
             Node p = node;
             while(p!=null || !stack.isEmpty()){
                while(p!=null){
                   stack.push(p);
                   p = p.getLeft();
              }
               if(!stack.isEmpty()){ 
                      p = stack.pop();
                         System.out.print(p.getValue()+" ");
                     p = p.getRight();
                }
          }
     }
    
    八、非递归后序遍历
    后序遍历相比前面的前序遍历和中序遍历在编程这里会难一点,不过理解了思想,看代码还是没有什么问题的
    
     public void postOrderTraversalbyLoop(Node node){
    
              Stack<Node> stack = new Stack<Node>();
             Node p = node,    prev = node;
             while(p!=null || !stack.isEmpty()){
                    while(p!=null){
                         stack.push(p);
                         p = p.getLeft();
                  }
                   if(!stack.isEmpty()){
                         Node temp = stack.peek().getRight();
                         //只是拿出来栈顶这个值,并没有进行删除
                         if(temp == null||temp == prev){
                           //节点没有右子节点或者到达根节点【考虑到最后一种情况】
                            p = stack.pop();
                           System.out.print(p.getValue()+" ");
                           prev = p;
                           p = null;
                           }
                           else{
                                    p = temp;
                                    } 
                     }
           }
     }
    
    最后就可以放大招了,来看看广度优先遍历和深度优先遍历吧
    
    九、广度优先遍历
     public void bfs(Node root){
      if(root == null) return;
      LinkedList<Node> queue = new LinkedList<Node>();
      queue.offer(root); //首先将根节点存入队列
      //当队列里有值时,每次取出队首的node打印,打印之后判断node是否有子节点,若有,则将子节点加入队列
      while(queue.size() > 0){ 
      Node node = queue.peek();
       queue.poll(); //取出队首元素并打印
       System.out.print(node.var+" ");
       if(node.left != null){ //如果有左子节点,则将其存入队列
        queue.offer(node.left);
       }
       if(node.right != null){ //如果有右子节点,则将其存入队列
        queue.offer(node.right);
       }
      }
     }
    
    九、深度优先遍历
    public void dfs(Node node,List<List<Integer>> rst,List<Integer> list){
     if(node == null) return;
     if(node.left == null && node.right == null){
      list.add(node.var);
      /* 这里将list存入rst中时,不能直接将list存入,而是通过新建一个list来实现,
      * 因为如果直接用list的话,后面remove的时候也会将其最后一个存的节点删掉
      * */
      rst.add(new ArrayList<>(list));
      list.remove(list.size()-1);
     }
     list.add(node.var);
     dfs(node.left,rst,list);
     dfs(node.right,rst,list);
     list.remove(list.size()-1);
     }
    
  • 相关阅读:
    Windows 7 NVMe补丁(包括官网下载链接)
    最近做的项目的数据处理及可视化小结
    debugging tools for windows 10下载安装问题
    QT安装过程中出现的问题
    Datasnap 中序列化和反序列化
    C# POST multipart/form-data 方式提交数据
    Delphi的前景
    Delphi 禁止重复运行同一程序的方法
    Delphi7 打开工程报错 '找不到指定的模块'&'EOIesysError'
    Delphi7 “cannot Create output File ..inproject1.exe”
  • 原文地址:https://www.cnblogs.com/cfas/p/12880976.html
Copyright © 2011-2022 走看看