首页 > 编程知识 正文

树的三种遍历方式,树的三种主要遍历方法

时间:2023-05-06 18:55:09 阅读:229184 作者:1846

1.树的中序遍历非递归 思路使用stack替换掉递归(第一种写法 )

第一种写法是

while(两个条件) while(条件一) if(条件二)

1 . while(两种情况)
- 直走到树的最左边结点,把左边的结点全部压入stack,
- 走完左边的结点后,出stack, 继判断是否最左边的结点是否有右结点
== 如果有右结点,则对这个子树执行(1)中同样的操作,回到步骤一==

2.出stack的同时访问结点

代码(Java) // Definition for a binary tree node. public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } }class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> result =new ArrayList<>(); if(root==null) return result; Stack<TreeNode> tmp = new Stack<>(); TreeNode p = root; //p是遍历指针,如果一直有最左边结点,一直遍历; 第二种情况是如果没有最左边结点,就要访问stack中的结点 while(p!=null || !tmp.isEmpty()) { //两种情况 //第一种情况,来到最左边的结点,最左边的结点肯定是没有左孩子结点 while (p != null) { tmp.add(p); p = p.left; } //判断最左边的结点是否有右结点,继续按照上面的步骤遍历遍历到其最左边的结点 if (!tmp.isEmpty()) { //遍历最左边的结点 TreeNode t = tmp.pop(); result.add(t.val); if (t.right != null) p = t.right; } } return result; }} c++ class Solution {public:vector<int> inorderTraversal(TreeNode* root) { stack<TreeNode*> tmp; vector<int> result; if(root==nullptr) result; TreeNode * p= root; while(p!=nullptr || !tmp.empty()){ while(p!=nullptr){ tmp.push(p); p = p->left; } if(!tmp.empty()){ //注意在这里先获取然后删除,两个步骤 TreeNode *t= tmp.top(); tmp.pop(); result.push_back(t->val); if(t->right!=nullptr) p = t->right; } } return result; }}; 二叉树的前序遍历 第一种写法

使用stack,先押入根节点,然后逐步出根节点访问根节点,判断左右的情况,右节押入栈, 然后再押入左节点,再出栈即可先实现访问左节点,然后再访问右节点情况。
在这里同样需要

先压入
步骤1 :压入1
步骤2: 弹出1, 访问1,判断左右的情况 压入 3,2
步骤3:弹出2, 访问2,判断左右儿子节点是否为空,压入 5,4 ,栈中元素变成3,5,4
步骤:弹出 4, 访问 4, 然后4的左右子节点为空,所以不加入
步骤:弹出5, 访问5, 然后5的左右节点为空, 所以不加入
步骤:弹出3,访问3 ,然后加入 7, 6
步骤 :弹出6, 访问 6 不加入
步骤: 弹出 7, 不加入 ,
栈为空, 程序停止

代码如下

public class BinaryTreePreorderTraversal { //第一中写法的情况,押入就处理处理一个 public List<Integer> solution(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); List<Integer> result = new ArrayList<>(); if (root == null){ return result;} stack.add(root); while (!stack.isEmpty()) { TreeNode pre = stack.pop(); result.add(pre.val); if (pre.right != null) { stack.add(pre.right); } if (pre.left != null) { stack.add(pre.left); } return result; }

其实该中方法跟层次遍历采用的队列很像,先将根节点入stack,然后在进入while
循环, 弹出来,访问根节点,判断左右节点是否空,如果不为空,就加入进来,只不过在这里stack先入rightchild , 然后再入leftchild

第二种写法 第一种写法采用类似中序遍历的写法,只不过对while- while - if–中的循环条件进行更改,在第二个while循环中入stack 之前,进行访问,然后if执行对右边节点进行1同样的操作

其实这种方法跟中序遍历是一样的做法,只不过在这个地方访问节点的顺序改变了
前序遍历在入第二个while ,入stack 之前访问节点
中序遍历在if语句中访问节点 , 然后再对右节点执行同样的操作即可,不用判断右节点是否为空,已经在前面开头就已经判断了所以不用再进行判断即可

代码 /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */class Solution { public List<Integer> preorderTraversal(TreeNode root) { List<Integer> result= new ArrayList<>(); if(root==null) return result; Stack<TreeNode> stack =new Stack<>(); TreeNode p=root; while(!stack.isEmpty()||p!=null){ while(p!=null){ // zyihi1 result.add(p.val); // 注意下面两个指令的执行顺序问题,访问完之后是直接压入stack的情况 stack.push(p); p = p.left; } if(!stack.isEmpty()){ p = stack.pop(); //注意在这里不要写成 先判断p是否有右孩子节点再做,比如,下面注解部分是错误的解法, /*if(p.right!=null) { p = p.right; }*/ p = p.right; } } return result; }} 前序遍历算法的应用

114. Flatten Binary Tree to Linked List
总共有几种写法,特别是先根遍历阶段情况

后根遍历的非递归方法

类似于中序遍历,左边的节点都是要第一个进行访问,在这里只是右节点和根节点的区别,同样采用一个栈来解决这个问题。只是

后序遍历在决定是否可以输出当前节点的值的时候,需要考虑其左右子树是否都已经遍历完成。

所以需要设置一个lastVisit游标。

若lastVisit等于当前考查节点的右子树,表示该节点的左右子树都已经遍历完成,则可以输出当前节点。

并把lastVisit节点设置成当前节点,将当前游标节点node设置为空,下一轮就可以访问栈顶元素。

以下考虑后序遍历中的三种情况

如图一所示,从节点1开始考查直到节点4的左子树为空。
注:此时的游标节点node = 4.left == null。
此时需要从栈中查看 Peek()栈顶元素。
发现节点4的右子树非空,需要接着考查右子树,4不能输出,node = node.right。

如图2所示,考查到节点7(7.left == null,7是从栈中弹出),其左右子树都为空,可以直接输出7。

此时需要把lastVisit设置成节点7,并把游标节点node设置成null,下一轮循环的时候会考查栈中的节点6。

考查完节点8之后(lastVisit == 节点8),将游标节点node赋值为栈顶元素6,节点6的右子树正好等于节点8。表示节点6的左右子树都已经遍历完成,直接输出6。
此时,可以将节点直接从栈中弹出Pop(),之前用的只是Peek()。
将游标节点node设置成null。

否者,需要接着考虑右子树,node = node.right。(没有访问的)

class Solution { public List<Integer> postorderTraversal(TreeNode root) { List<Integer> result =new LinkedList<>(); Stack<TreeNode> stack = new Stack<>(); TreeNode p = root; TreeNode listVisited= root; while(p!=null || !stack.isEmpty()){ while(p!=null){ //一直走到最左边的节点,这个节点肯定要第一个访问的,类似与中序遍历中一样,中序遍历与后序遍历的唯一差别就是,根节点和右孩子节点的区别而已 stack.push(p); p = p.left; } //获取到最左的情况 p = stack.peek(); //p是叶子节点或者p是访问的上面的 if(p.right==null || p.right==listVisited){ //可以访问p的值l; result.add(p.val); listVisited = p; stack.pop(); //这里一定要注意,一定要重置为null,否则p不为空的话继续执行压入栈处理操作, //p==null 是执行出栈处理的情况 p = null; } else{ p =p.right; } } return result; }}

参考链接

层次遍历

使用一个队列来解决这个问题
如果是直接打印即可的那种方式的话,

class Solution { public: vector<int> levelOrder(TreeNode* root) { vector<int> ret; queue<TreeNode*> q; if(root) q.push(root); while(!q.empty()) { TreeNode* temp = q.front(); ret.push_back(temp->val); q.pop(); if(temp->left) q.push(temp->left); if(temp->right) q.push(temp->right); } return ret; } };

如果是每层都分开打印

计算每层的打一个

计算每层打印的次数即可

层次遍历跟先序遍历一样的代码体,只不过在这里使用队列替换成stack即可

总结

三种算法比较好的思路总结

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。