• 数据结构5_java---二叉树,树的建立,树的先序、中序、后序遍历(递归和非递归算法),层次遍历(广度优先遍历),深度优先遍历,树的深度(递归算法)


    1、二叉树的建立

      首先,定义数组存储树的data,然后使用list集合将所有的二叉树结点都包含进去,最后给每个父亲结点赋予左右孩子。

    需要注意的是:最后一个父亲结点需要单独处理

     1 public static TreeNode root;
     2     //建立二叉树内部类
     3     class TreeNode{
     4         public Object data;                    //携带变量
     5         public TreeNode lchild,rchild;         //左右孩子
     6         public TreeNode() {
     7             data = null;
     8             lchild = null;
     9             rchild = null;
    10         }
    11         public TreeNode(Object data) {
    12             this.data = data;
    13         }
    14         public void setLeft(TreeNode left)
    15         {
    16             this.lchild = left;
    17         }
    18         public void setRight(TreeNode right)
    19         {
    20             this.rchild = right;
    21         }
    22     }
    23             //建立二叉树,通过List作为中间过渡量
    24         public void CreatBinTree(int []datas,List<TreeNode> nodelist) {
    25             for(int i=0;i<datas.length;i++)
    26             {
    27                 TreeNode aNode = new TreeNode(datas[i]);
    28                 nodelist.add(aNode);
    29             }
    30             //给所有父亲结点设定子节点
    31             for(int index = 0;index<nodelist.size()/2-1;index++)
    32             {
    33                 //在起始结点为0时,为N的父亲结点他的左孩子为2*N+1,右孩子为2*N+2
    34                 nodelist.get(index).lchild = nodelist.get(2*index+1);
    35                 nodelist.get(index).rchild = nodelist.get(2*index+2);
    36             }
    37             //单独处理最后一个父亲结点
    38             int index = nodelist.size()/2-1;
    39             nodelist.get(index).lchild = nodelist.get(2*index+1);
    40             if(nodelist.size()%2==1)
    41                 nodelist.get(index).rchild = nodelist.get(2*index+2);
    42         }                

    2、先序遍历

    2.1 递归方法

    //先序遍历,递归操作
            public void PreOrder(TreeNode root)
            {
                if(root == null)
                    return ;
                System.out.print(root.data+" ");
                PreOrder(root.lchild);
                PreOrder(root.rchild);
            }

    2.2 非递归操作,使用栈(两种方法)

    本人认为第一种方法相对容易理解,较为简单

    方法一:

    思想:
       (1)若栈非空或者结点非空,则进入循环
       (2)若结点非空,读取该节点,并将结点压入栈中,结点向左孩子移动,移到最左孩子
       (3)若栈非空,取出栈顶元素,此时父节点已读,所以移向右孩子
    ****************核心为:边读取左结点变将其压入栈中,左孩子对下一级来说上也代表父亲结点,所以之后可以直接读取右孩子。*******************

             /*
             * 方法一:先序遍历,使用栈操作,相对较简单
             * 思想:
             * (1)若栈非空或者结点非空,则进入循环
             * (2)若结点非空,读取该节点,并将结点压入栈中,结点向左孩子移动,移到最左孩子
             * (3)若栈非空,取出栈顶元素,此时父节点已读,所以移向右孩子
             * 核心为:边读取左结点变将其压入栈中,左孩子对下一级来说上也代表父亲结点,所以之后可以直接读取右孩子。
             */
            public void preOrder1()
            {
                Stack<TreeNode> stack = new Stack<>();
                TreeNode node = root;
                while(!stack.isEmpty()||node!=null)
                {
                    while(node!=null)
                    {
                        System.out.print(node.data+" "); //访问该节点
                        stack.push(node);
                        node = node.lchild;
                    }
                    if(!stack.isEmpty())
                    {
                        node = stack.pop();
                        node = node.rchild;
                    }
                }
            }

    方法二:

    思想:
     (1)首先将根节点入栈
     (2)判断非空,将结点从栈中取出并访问
     (3)依次访问栈中的左孩子,并将右孩子放入栈中,结点不断往左孩子移动
     (4)重复步骤(2)(3),直到栈为空

              /*
             * 方法二:先序遍历,使用栈操作,相对较麻烦
             * 思想:
             * (1)首先将根节点入栈
             * (2)判断非空,将结点从栈中取出并访问
             * (3)依次访问栈中的左孩子,并将右孩子放入栈中,结点不断往左孩子移动
             * (4)重复步骤(2)(3),直到栈为空
             */
            public void preOrder2() throws Exception
            {
                TreeNode pNode = root;
                if(pNode!=null)       //首先判断根结点是否为空
                {
                    Stack<TreeNode> astack = new Stack<>();     //构造栈操作 
                    astack.push(pNode);            //将根节点压入栈中
                    while(!astack.isEmpty())        //循环操作直到栈中没有结点存在,即最右结点访问完毕
                    {
                        pNode = astack.pop();               //首先将结点从栈中取出
                        System.out.print(pNode.data+" "); //访问该节点
                        while(pNode!=null)                  //若该结点不为空    
                        {
                            if(pNode.lchild!=null)         //访问左子树
                                System.out.print(pNode.lchild.data+" ");
                            if(pNode.rchild!=null)         //将右子树压入栈中
                                astack.push(pNode.rchild);
                            pNode = pNode.lchild;          //进入到下一个左子树中
                        }
                    }
                    
                }
            }

    3、中序遍历

    3.1 递归操作

    //中序遍历,递归操作
            public void inOrder(TreeNode root)
            {
                if(root==null)
                    return ;
                inOrder(root.lchild);
                System.out.print(root.data+" ");
                inOrder(root.rchild);
            }

    3.2 非递归操作,栈

    思想:
    * (1)若栈非空或者结点非空,则进入循环
    * (2)若结点非空,则将结点压入栈中,结点向左孩子移动,一直到最左边
    * (3)若栈非空,则取出栈顶元素,并读取访问数据,而后结点向右孩子移动

    /*
             * 中序遍历,使用栈操作
             * 思想:
             * (1)若栈非空或者结点非空,则进入循环
             * (2)若结点非空,则将结点压入栈中,结点向左孩子移动,一直到最左边
             * (3)若栈非空,则取出栈顶元素,并读取访问数据,而后结点向右孩子移动
             */
            public void inOrder1() throws Exception
            {
                TreeNode node = root;
                Stack<TreeNode> stack = new Stack<>();
                while(node!=null||!stack.isEmpty())
                {
                    while(node!=null)
                    {
                        stack.push(node);
                        node = node.lchild;
                    }
                    if(stack!=null)
                    {
                        node = stack.pop();
                        System.out.print(node.data+" ");
                        node = node.rchild;
                    }
                }
            }

    4、后序遍历

    4.1 递归操作

    //后序遍历,递归操作
            public void postOrder(TreeNode root)
            {
                if(root==null)
                    return ;
                postOrder(root.lchild);
                postOrder(root.rchild);
                System.out.print(root.data+" ");
            }

    4.2 非递归操作

    前提:设置标志结点pre,指示是否访问过某结点
    思想:
    *  (1)若栈非空或者结点非空,则进入循环
    *  (2)若结点非空,则将结点压入栈中,结点向左孩子移动,一直到最左边
    *  (3)若栈非空,首先取出栈顶元素的右孩子赋给tmp
       1、若栈顶元素的右孩子为空或者等于pre(即已访问过),则弹出元素并访问,将该结点赋值给pre,并将当前结点赋值为null
       2、否则的话将右孩子赋值给当前结点

    /*
             * 后序遍历,栈操作
             * 前提:设置标志结点pre,指示是否访问过某结点
             * 思想:
             * (1)若栈非空或者结点非空,则进入循环
             * (2)若结点非空,则将结点压入栈中,结点向左孩子移动,一直到最左边
             * (3)若栈非空,首先取出栈顶元素的右孩子赋给tmp
             *         1、若栈顶元素的右孩子为空或者等于pre(即已访问过),则弹出元素并访问,将该结点赋值给pre,并将当前结点赋值为null
             *      2、否则的话将右孩子赋值给当前结点
             */
            public void postOrder1()
            {
                Stack<TreeNode> stack = new Stack<>();
                TreeNode node = root,pre = root;
                while(!stack.isEmpty()||node!=null)
                {
                    while(node!=null)
                    {
                        stack.push(node);
                        node = node.lchild;
                    }
                    if(!stack.isEmpty())
                    {
                        TreeNode tmp = stack.peek().rchild;
                        if(tmp==null||tmp==pre)
                        {
                            node = stack.pop();
                            System.out.print(node.data+" ");
                            pre = node;
                            node = null;
                        }else {
                            node = tmp;
                        }
                        
                    }
                }
                
            }

    5、层次遍历(广度优先遍历)使用队列

    思想:
    * (1)读取根节点,并将其压入队列中
    * (2)以队列的长度作为循环的判断条件,取出队收元素并访问,访问后将其弹出
    * (3)判断是否有左右孩子,若有则加入队列中。

    public void bfs()
            {
                Queue<TreeNode> queue = new LinkedList();
                if(root==null)
                    return ;
                queue.offer(root);
                while(queue.size()>0) {
                    TreeNode node = queue.peek();
                    queue.poll();
                    System.out.print(node.data+" ");
                    if(node.lchild!=null)
                    {
                        queue.offer(node.lchild);
                    }
                    if(node.rchild!=null)
                    {
                        queue.offer(node.rchild);
                    }
                }
                
            }

    6、深度优先遍历

    此部分相对较难理解,自行琢磨

    public void dfs(TreeNode node,List<List<Integer>> nList,List<Integer>  list)
            {
                if(node==null)
                    return ;
                if(node.lchild==null&node.rchild==null)
                {
                    list.add((Integer) node.data);
                    nList.add(new ArrayList<>(list));
                    list.remove(list.size()-1);
                }
                list.add((Integer) node.data);
                dfs(node.lchild, nList, list);
                dfs(node.rchild, nList, list);
                list.remove(list.size()-1);
            }

    7、求树的深度

    (1)若二叉树为空,则返回0

    (2)若二叉树非空,求左子树的深度,求右子树的深度

    (3)比较左右子树的深度,求最大值加1,即为二叉树的深度

    //求二叉树的深度
            public int Depth(TreeNode node)
            {
                if(node==null)
                    return 0;
                else {
                    int ldepth = Depth(node.lchild);
                    System.out.println("node'data:"+node.data+"ldepth: "+ldepth);
                    int rdepth = Depth(node.rchild);
                    System.out.println("node'data:"+node.data+"rdepth: "+rdepth+" ");
                    if(ldepth<rdepth)
                        return rdepth+1;
                    else
                        return ldepth+1;
                }
            }

    7、应用及全部代码展示

      1 package Main;
      2 
      3 import java.util.ArrayList;
      4 import java.util.Collection;
      5 import java.util.Iterator;
      6 import java.util.LinkedList;
      7 import java.util.List;
      8 import java.util.Queue;
      9 import java.util.Scanner;
     10 import java.util.Stack;
     11 
     12 import javax.naming.directory.SearchControls;
     13 public class Main{
     14     public static TreeNode root;
     15     //建立二叉树内部类
     16     class TreeNode{
     17         public Object data;                    //携带变量
     18         public TreeNode lchild,rchild;         //左右孩子
     19         public TreeNode() {
     20             data = null;
     21             lchild = null;
     22             rchild = null;
     23         }
     24         public TreeNode(Object data) {
     25             this.data = data;
     26         }
     27         public void setLeft(TreeNode left)
     28         {
     29             this.lchild = left;
     30         }
     31         public void setRight(TreeNode right)
     32         {
     33             this.rchild = right;
     34         }
     35     }
     36     //建立二叉树,通过List作为中间过渡量
     37         public void CreatBinTree(int []datas,List<TreeNode> nodelist) {
     38             for(int i=0;i<datas.length;i++)
     39             {
     40                 TreeNode aNode = new TreeNode(datas[i]);
     41                 nodelist.add(aNode);
     42             }
     43             //给所有父亲结点设定子节点
     44             for(int index = 0;index<nodelist.size()/2-1;index++)
     45             {
     46                 //在起始结点为0时,为N的父亲结点他的左孩子为2*N+1,右孩子为2*N+2
     47                 nodelist.get(index).lchild = nodelist.get(2*index+1);
     48                 nodelist.get(index).rchild = nodelist.get(2*index+2);
     49             }
     50             //单独处理最后一个父亲结点
     51             int index = nodelist.size()/2-1;
     52             nodelist.get(index).lchild = nodelist.get(2*index+1);
     53             if(nodelist.size()%2==1)
     54                 nodelist.get(index).rchild = nodelist.get(2*index+2);
     55         }
     56         //先序遍历,递归操作
     57         public void PreOrder(TreeNode root)
     58         {
     59             if(root == null)
     60                 return ;
     61             System.out.print(root.data+" ");
     62             PreOrder(root.lchild);
     63             PreOrder(root.rchild);
     64         }
     65         /*
     66          * 方法一:先序遍历,使用栈操作,相对较简单
     67          * 思想:
     68          * (1)若栈非空或者结点非空,则进入循环
     69          * (2)若结点非空,读取该节点,并将结点压入栈中,结点向左孩子移动,移到最左孩子
     70          * (3)若栈非空,取出栈顶元素,此时父节点已读,所以移向右孩子
     71          * 核心为:边读取左结点变将其压入栈中,左孩子对下一级来说上也代表父亲结点,所以之后可以直接读取右孩子。
     72          */
     73         public void preOrder1()
     74         {
     75             Stack<TreeNode> stack = new Stack<>();
     76             TreeNode node = root;
     77             while(!stack.isEmpty()||node!=null)
     78             {
     79                 while(node!=null)
     80                 {
     81                     System.out.print(node.data+" "); //访问该节点
     82                     stack.push(node);
     83                     node = node.lchild;
     84                 }
     85                 if(!stack.isEmpty())
     86                 {
     87                     node = stack.pop();
     88                     node = node.rchild;
     89                 }
     90             }
     91         }
     92         /*
     93          * 方法二:先序遍历,使用栈操作,相对较麻烦
     94          * 思想:
     95          * (1)首先将根节点入栈
     96          * (2)判断非空,将结点从栈中取出并访问
     97          * (3)依次访问栈中的左孩子,并将右孩子放入栈中,结点不断往左孩子移动
     98          * (4)重复步骤(2)(3),直到栈为空
     99          */
    100         public void preOrder2() throws Exception
    101         {
    102             TreeNode pNode = root;
    103             if(pNode!=null)       //首先判断根结点是否为空
    104             {
    105                 Stack<TreeNode> astack = new Stack<>();     //构造栈操作 
    106                 astack.push(pNode);            //将根节点压入栈中
    107                 while(!astack.isEmpty())        //循环操作直到栈中没有结点存在,即最右结点访问完毕
    108                 {
    109                     pNode = astack.pop();               //首先将结点从栈中取出
    110                     System.out.print(pNode.data+" "); //访问该节点
    111                     while(pNode!=null)                  //若该结点不为空    
    112                     {
    113                         if(pNode.lchild!=null)         //访问左子树
    114                             System.out.print(pNode.lchild.data+" ");
    115                         if(pNode.rchild!=null)         //将右子树压入栈中
    116                             astack.push(pNode.rchild);
    117                         pNode = pNode.lchild;          //进入到下一个左子树中
    118                     }
    119                 }
    120                 
    121             }
    122         }
    123         //中序遍历,递归操作
    124         public void inOrder(TreeNode root)
    125         {
    126             if(root==null)
    127                 return ;
    128             inOrder(root.lchild);
    129             System.out.print(root.data+" ");
    130             inOrder(root.rchild);
    131         }
    132         /*
    133          * 中序遍历,使用栈操作
    134          * 思想:
    135          * (1)若栈非空或者结点非空,则进入循环
    136          * (2)若结点非空,则将结点压入栈中,结点向左孩子移动,一直到最左边
    137          * (3)若栈非空,则取出栈顶元素,并读取访问数据,而后结点向右孩子移动
    138          */
    139         public void inOrder1() throws Exception
    140         {
    141             TreeNode node = root;
    142             Stack<TreeNode> stack = new Stack<>();
    143             while(node!=null||!stack.isEmpty())
    144             {
    145                 while(node!=null)
    146                 {
    147                     stack.push(node);
    148                     node = node.lchild;
    149                 }
    150                 if(stack!=null)
    151                 {
    152                     node = stack.pop();
    153                     System.out.print(node.data+" ");
    154                     node = node.rchild;
    155                 }
    156             }
    157         }
    158         //后序遍历,递归操作
    159         public void postOrder(TreeNode root)
    160         {
    161             if(root==null)
    162                 return ;
    163             postOrder(root.lchild);
    164             postOrder(root.rchild);
    165             System.out.print(root.data+" ");
    166         }
    167         /*
    168          * 后序遍历,栈操作
    169          * 前提:设置标志结点pre,指示是否访问过某结点
    170          * 思想:
    171          * (1)若栈非空或者结点非空,则进入循环
    172          * (2)若结点非空,则将结点压入栈中,结点向左孩子移动,一直到最左边
    173          * (3)若栈非空,首先取出栈顶元素的右孩子赋给tmp
    174          *         1、若栈顶元素的右孩子为空或者等于pre(即已访问过),则弹出元素并访问,将该结点赋值给pre,并将当前结点赋值为null
    175          *      2、否则的话将右孩子赋值给当前结点
    176          */
    177         public void postOrder1()
    178         {
    179             Stack<TreeNode> stack = new Stack<>();
    180             TreeNode node = root,pre = root;
    181             while(!stack.isEmpty()||node!=null)
    182             {
    183                 while(node!=null)
    184                 {
    185                     stack.push(node);
    186                     node = node.lchild;
    187                 }
    188                 if(!stack.isEmpty())
    189                 {
    190                     TreeNode tmp = stack.peek().rchild;
    191                     if(tmp==null||tmp==pre)
    192                     {
    193                         node = stack.pop();
    194                         System.out.print(node.data+" ");
    195                         pre = node;
    196                         node = null;
    197                     }else {
    198                         node = tmp;
    199                     }
    200                     
    201                 }
    202             }
    203             
    204         }
    205         /*层次遍历,即广度优先遍历,从上到下遍历二叉树
    206          * 思想:
    207          * (1)读取根节点,并将其压入队列中
    208          * (2)以队列的长度作为循环的判断条件,取出队收元素并访问,访问后将其弹出
    209          * (3)判断是否有左右孩子,若有则加入队列中。
    210          * */
    211         public void bfs()
    212         {
    213             Queue<TreeNode> queue = new LinkedList();
    214             if(root==null)
    215                 return ;
    216             queue.offer(root);
    217             while(queue.size()>0) {
    218                 TreeNode node = queue.peek();
    219                 queue.poll();
    220                 System.out.print(node.data+" ");
    221                 if(node.lchild!=null)
    222                 {
    223                     queue.offer(node.lchild);
    224                 }
    225                 if(node.rchild!=null)
    226                 {
    227                     queue.offer(node.rchild);
    228                 }
    229             }
    230             
    231         }
    232         /*
    233          * 深度优先遍历,从左到右遍历二叉树
    234          * */
    235         public void dfs(TreeNode node,List<List<Integer>> nList,List<Integer>  list)
    236         {
    237             if(node==null)
    238                 return ;
    239             if(node.lchild==null&node.rchild==null)
    240             {
    241                 list.add((Integer) node.data);
    242                 nList.add(new ArrayList<>(list));
    243                 list.remove(list.size()-1);
    244             }
    245             list.add((Integer) node.data);
    246             dfs(node.lchild, nList, list);
    247             dfs(node.rchild, nList, list);
    248             list.remove(list.size()-1);
    249         }
    250         //查找算法
    251         public static TreeNode Searchparameter(TreeNode root,int x)
    252         {
    253             TreeNode node = root;
    254             if(root==null)
    255                 return null;
    256             else {
    257                 if(node.data.equals(x))
    258                     return node;
    259                 else {
    260                     TreeNode aNode = Searchparameter(node.lchild, x);
    261                     if(aNode==null)
    262                         return Searchparameter(node.rchild, x);
    263                     else 
    264                         return aNode;
    265                 }
    266             }
    267         }
    268         //求二叉树的深度
    269         public int Depth(TreeNode node)
    270         {
    271             if(node==null)
    272                 return 0;
    273             else {
    274                 int ldepth = Depth(node.lchild);        
    275                 int rdepth = Depth(node.rchild);
    276                 if(ldepth<rdepth)
    277                     return rdepth+1;
    278                 else
    279                     return ldepth+1;
    280             }
    281         }
    282         public static void main(String[] args) throws Exception {
    283             int datas[] = {1,2,3,4,5,6,7,8,9,10};
    284             List<TreeNode> nodelist = new LinkedList<>();
    285             Main aMain = new Main();
    286             aMain.CreatBinTree(datas,nodelist);
    287             root = nodelist.get(0);
    288             
    289             System.out.println("First order traversal(recursive):");
    290             aMain.PreOrder(root);
    291             System.out.println();
    292             System.out.println("First order traversal(stack--1):");
    293             aMain.preOrder1();
    294             System.out.println();
    295             System.out.println("First order traversal(stack--2):");
    296             aMain.preOrder2();
    297             System.out.println();
    298             System.out.println("Inorder traversal(recursive):");
    299             aMain.inOrder(root);
    300             System.out.println();
    301             System.out.println("Inorder traversal(stack--1):");
    302             aMain.inOrder1();
    303             System.out.println();
    304             System.out.println("Postorder traversal(recursive):");
    305             aMain.postOrder(root);
    306             System.out.println();
    307             System.out.println("Postorder traversal(stack--1):");
    308             aMain.postOrder1();
    309             System.out.println();
    310             System.out.println("Level traversal(queue--1):");
    311             aMain.bfs();
    312             
    313             List<List<Integer>> rst = new ArrayList<>();
    314             List<Integer> list = new ArrayList<>();
    315             System.out.println();
    316             System.out.println("Depth first traversal(queue--1):");
    317             aMain.dfs(root,rst,list);
    318             System.out.println(rst);
    319             int x = 6;
    320             TreeNode aNode = Searchparameter(root,x);
    321             System.out.println(aNode.data);
    322             
    323             int depth = aMain.Depth(root);
    324             System.out.println("the depth of the tree is :"+depth);
    325         }
    326         
    327     
    328 }

                      结果展示:

  • 相关阅读:
    MongoDB集群架构 调整,增加延迟备份节点服务器,删除仲裁节点(9)
    MongoDB 副本集管理-不定期更新(8)
    DML和查询操作说明(7)
    MongoDB 副本集权限认证(6)
    python之路——作业:Select FTP(仅供参考)
    python之路——作业:类Farbic主机管理程序(仅供参考)
    python之路——作业:高级FTP(仅供参考)
    python之路——RabbitMQ
    python之路——协程(greenlet、gevent、简单爬虫)
    python之路——多进程(进程间通信、进程池)
  • 原文地址:https://www.cnblogs.com/liuhui5599/p/8662627.html
Copyright © 2020-2023  润新知