树的遍历及其应用

二叉树的遍历:

一、递归式遍历:

1.树的递归式遍历的顺序:

 

分析:从中我们可以看出每个节点都会被遍历到三遍

2.代码实现(包括树的结构):

 1  /*
 2       * 树的基本结构定义
 3       */
 4     class Node {
 5         int val;
 6         Node left;
 7         Node right;
 8     }
 9     
10     //树的递归式遍历:
11     public static void orderTraver(Node t) {
12         if (t == null) {
13             return;
14         }
15         orderTraver(t.left);
16         orderTraver(t.right);
17     }

 

3.前中后序遍历(深度优先搜索):

前置知识点:前中后序遍历概念

(1).前序遍历:对于任何一个子树,总是先遍历头节点,再遍历左子树,最后遍历右子树

(2).中序遍历:对于任何一个子树,总是先遍历左子树,再遍历头节点,最后遍历右子树

(3).后序遍历:对于任何一个子树,总是先遍历左子树,再遍历右子树,最后遍历头节点

 

在递归序遍历中实现前中序遍历的操作:

根据上述知识点已知递归序遍历会经历同一个节点3次
(1).前序遍历:在第1次遍历到节点时,执行所要进行的操作

代码:

1 //递归式前序遍历
2     public static void preOrderTraver(Node t) {
3         if (t == null) {
4             return;
5         }
6         System.out.print(t.val + " ");//执行所对应的遍历操作
7         preOrderTraver(t.left);
8         preOrderTraver(t.right);
9     }

 

(2).中序遍历:在第2次遍历到节点时,执行所要进行的操作

 代码:

 1 //递归式中序遍历
 2     public static void inOrderTraver(Node t) {
 3         if (t == null) {
 4             return;
 5         }
 6         inOrderTraver(t.left);
 7         System.out.print(t.val + " ");//执行所对应的遍历操作
 8         inOrderTraver(t.right);
 9     }
10     

 

(3).后序遍历:在第3次遍历到节点时,执行所要进行的操作

代码:

1 //递归式后序遍历
2     public static void posOrderTraver(Node t) {
3         if (t == null) {
4             return;
5         }
6         posOrderTraver(t.left);
7         posOrderTraver(t.right);
8         System.out.print(t.val + " ");//执行所对应的遍历操作
9     }

 

2.非递归遍历

(1).前序遍历(自己手动实现压栈):

* 预先准备:先把根节点压到栈中
* 1.从栈中弹出一个节点并输出
* 2.将这个节点的右儿子和左儿子先后压入栈中(如果存在的话)
* 重复以上2个步骤即可

 1 public static void preOrderTraver1(Node head) {
 2         if (head == null) {
 3             return;
 4         }
 5         Stack<Node> sk = new Stack<>();
 6         sk.push(head);
 7         while (sk.size() > 0) {
 8             System.out.print(sk.peek().val + " ");
 9             Node t = sk.pop();
10             if (t.right != null) {
11                 sk.push(t.right);
12             }
13             if (t.left != null) {
14                 sk.push(t.left);
15             }
16         }
17         System.out.println();
18     }

 

(2).中序遍历:

* 处理操作:
* 1.先把所在节点的左儿子(以及左儿子的左儿子(一直递推,直到找不到左儿子为止))弹入节点
* 2.之后陆续弹出这些节点,并在弹出的过程中寻找这些节点有没有右树,如果有右树,则对这颗右树继续执行1,2操作
* 不断执行以上的操作,直到栈空

 1 public static void inOrderTraver1(Node head) {
 2         if (head == null) {
 3             return;
 4         }
 5         Stack<Node> sk = new Stack<>();
 6         while (sk.size() > 0 || head != null) {
 7             if (head != null) {//弹入该子树的所有左儿子(一定要把弹入左儿子的情况放if条件的前面)
 8                                //此处的if有点相当于while的作用,在head == null之前会一直往里弹
 9                 sk.push(head);
10                 head = head.left;
11             }
12             else {//弹入右儿子(子树)一定是在这个子树的左儿子都弹完的情况下才进行的
13                 head = sk.pop();
14                 System.out.print(head.val + " ");
15                 head = head.right;//下一次循环还是先去看它有没有左儿子
16             }
17         }
18         System.out.println();
19     }

 

(3).后续遍历:

* 非递归实现后续遍历:
* 预先准备:设好两个栈,原栈和收集栈,并将根节点弹入到原栈中
* 1.将原栈中顶层节点取出,压入到收集栈中
* 2.把该节点的左节点和右节点依次压入到栈中
* 重复以上2个步骤,直至原栈为空后,再依次把收集栈中的元素全部取出,得到的节点顺序即为后续遍历结果
*
* 分析:(以下简称:左儿子->左,右儿子->右,头节点->头)
* 因为后续遍历是左右头的顺序,但是在非递归遍历时,不好跨过头节点去直接遍历左右儿子
* 所以我们可以将左右头看成头右左的翻转(可以用栈实现翻转,只要进栈顺序是头右左,则出栈顺序就是所要的左右头)
* 所以此时收集栈的进栈顺序是头右左,所以原栈的出栈顺序就是头右左
* 此时只要调整好原栈的进出栈操作即可(与先序遍历思想类似)
*

 1 public static void posOrderTrever1(Node head) {
 2         Stack<Node> sk1 = new Stack<>();//原栈
 3         Stack<Node> sk2 = new Stack<>();//收集栈
 4         sk1.push(head);
 5         while (sk1.size() > 0) {
 6             Node t = sk1.pop();
 7             sk2.push(t);
 8             if (t.left != null) {
 9                 sk1.push(t.left);
10             }
11             if (t.right != null) {
12                 sk1.push(t.right);
13             }
14         }
15         while (sk2.size() > 0) {
16             System.out.print(sk2.pop().val + " ");
17         }
18         System.out.println();
19     }

 

将遍历的结果存储到线性表中:

 1 //将某种顺序的遍历序列内容存到一个线性表中(使用辅助结构List)
 2     //以中序遍历为例:
 3     public static void inOrderResult(Node root,List<Node> list) {
 4         if (root == null) {
 5             return;
 6         }
 7         inOrderResult(root.left,list);
 8         list.add(root);
 9         inOrderResult(root.right,list);
10     }

 

 

深度优先搜索的应用:
剑指 Offer 55 - I. 二叉树的深度 - 力扣(LeetCode) (leetcode-cn.com)

代码:

1 public int maxDepth(TreeNode root) {
2         if (root == null) {
3             return 0;
4         }
5         return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
6     }

 

 

3.广度(宽度)优先遍历:

* 二叉树的广度(宽度)优先遍历:
* 即按层级一层层往下遍历的方式
* 思路:用队列(先进先出的功能)进行维护,先进根节点,然后每次从队列中弹出一个节点
* 并分布弹入这个弹出节点的左节点和右节点即可.

 1 public static void wideOrderTraver(Node head) {
 2         if (head == null) {
 3             return;
 4         }
 5         Queue<Node> q = new LinkedList<>();//双向链表即为队列
 6         q.add(head);
 7         while (q.size() > 0) {
 8             Node t = q.poll();
 9             System.out.print(t.val + " ");
10             if (t.left != null) {
11                 q.add(t.left);
12             }
13             if (t.right != null) {
14                 q.add(t.right);
15             }
16         }
17     }

 

4.广度(宽度)优先遍历的应用:

* 寻找一颗树的最大宽度(即求出哪一层的节点个数最多,返回那个层数的节点个数)

 

思路:用广度优先搜索,按层级顺序依次往下遍历,再遍历的过程中,要注意什么时候换层并对每一层做好统计和处理即可,使用哈希表来辅助实现

代码及解析:

 1 public static int GetMaxWidth(Node head) {
 2         if (head == null) {
 3             return 0;
 4         }
 5         int max = Integer.MIN_VALUE;//用来存储最终的答案
 6         int curLevel = 1;//表示当前执行到哪个层了
 7         int curNodes = 0;//表示当前层次已有多少个节点
 8         Queue<Node> q = new LinkedList<>();
 9         HashMap<Node,Integer> hm = new HashMap<>();//记录节点所在的层数
10         q.add(head);
11         hm.put(head, 1);
12         while (q.size() > 0) {
13             Node t = q.poll();
14             int NodeLevel = hm.get(t);
15             if (NodeLevel == curLevel) {//还在当前层
16                 curNodes++;
17             }
18             else {
19                 max = Math.max(max, curNodes);//对上一层的总结点个数进行处理
20                 curLevel++;
21                 curNodes = 1;//注意此处是1,不要把这个节点给漏了
22             }
23             if (t.left != null) {
24                 q.add(t.left);
25                 hm.put(t.left, NodeLevel + 1);
26             }
27             if (t.right != null) {
28                 q.add(t.right);
29                 hm.put(t.right, NodeLevel + 1);
30             }
31         }
32         return max;
33     }

 

posted @ 2022-04-10 21:07  jue1e0  阅读(145)  评论(0)    收藏  举报