当前位置: 首页 > ds >正文

二叉树迭代遍历——给一个属性便可实现迭代结构完美统一

回顾二叉树的递归遍历

树结点

class TreeNode {TreeNode left;TreeNode right;int val;public TreeNode(TreeNode left, TreeNode right, int val) {this.left = left;this.right = right;this.val = val;}
}

前序递归遍历

public void preOrderRecur(Node head){if (head==null){return;}System.out.print(head.value+" ");preOrderRecur(head.left);preOrderRecur(head.right);
}

中序递归遍历

public void inOrderRecur(Node head){if (head==null){return;}inOrderRecur(head.left);System.out.print(head.value+" ");inOrderRecur(head.right);
}

后序递归遍历

public void postOrderRecur(Node head){if(head==null){return;}postOrderRecur(head.left);postOrderRecur(head.right);System.out.print(head.value+" ");
}

分析与思路

目标与期待

⊙ : \odot: :递归遍历非常方便,仅需移动输出head.value的顺序变可实现三种遍历,这是因为树的定义本来就是遵循递归所导致。因此,我们自然也想,树的非递归(迭代)遍历如果是这样就好了。

难点

⊙ : \odot: :首先自然得用到栈来模拟递归中涉及的变量栈。在非递归的条件下,前序遍历还是比较容易实现,只需打印head.value之后立马在栈中先后push进head.right和head.left即可(由于栈先进后出,因此我们后push进head.left,以便下一次先拿到是head.left)。

⊙ : \odot: :然而,但对中序遍历来说就特别麻烦,对于拿到的head,我们得先遍历完head的左子树才能输出head.value,之后才能push进head.right。换句话来说**,push进head.right和head.left应该是分开的,不能连在一起**,因此我们也得分两次拿到head以便分别push进head.right和head.left。

思路

⊙ : \odot: :因此,最关键的问题来了,拿到head之后,到底哪次该push进head.right,到底哪次该push进head.left。可以看到市面上很多的代码,各种额外的操作,其实本质就是在解决这个问题。

⊙ : \odot: :而我的想法就很简单,直接给树结点额外加个属性flag,记录其是第一次拿到head,还是第二次。添加属性flag之后,整个二叉树的迭代得到完美的统一。

代码实现

额外添加一个属性,但不改变原来TreeNode的结构。比如leetcode是无法改变提供好的TreeNode结构的,但我们可以自定义新的class。

class TreeNodeWithFlag {TreeNode treeNode;//flag为true,表明该节点可以直接输出,一开始为falseboolean flag;public TreeNodeWithFlag(TreeNode treeNode, boolean flag) {this.treeNode = treeNode;this.flag = flag;}
}

前序迭代遍历

    public static List<Integer> preorderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNodeWithFlag> st = new Stack<>();if (root != null) st.push(new TreeNodeWithFlag(root, false));while (!st.empty()) {TreeNodeWithFlag node = st.pop();if (!node.flag) {//flag为false,下面再将右中左节点添加到栈中// 添加右节点(空节点不入栈)if (node.treeNode.right != null) st.push(new TreeNodeWithFlag(node.treeNode.right, false));// 添加左节点(空节点不入栈)if (node.treeNode.left != null) st.push(new TreeNodeWithFlag(node.treeNode.left, false));//左右孩子已经处理过,令flag为true,表明下次访问到可以直接输出//对于前序遍历可以,其实下次循环必定就是输出node了node.flag=true;// 添加中节点st.push(node);//最终进栈时,保持右左中的顺序,出栈时保持中左右的顺序} else {//flag为true,表明该节点可以直接输出result.add(node.treeNode.val); // 加入到结果集}}return result;}

中序迭代遍历

    public static List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNodeWithFlag> st = new Stack<>();if (root != null) st.push(new TreeNodeWithFlag(root, false));while (!st.empty()) {TreeNodeWithFlag node = st.pop();if (!node.flag) {//flag为false,下面再将右中左节点添加到栈中// 添加右节点(空节点不入栈)if (node.treeNode.right != null) st.push(new TreeNodeWithFlag(node.treeNode.right, false));//右孩子已经处理过,令flag为true,表明下次访问到可以直接输出node.flag=true;// 添加中节点st.push(node);// 添加左节点(空节点不入栈)if (node.treeNode.left != null) st.push(new TreeNodeWithFlag(node.treeNode.left, false));//最终进栈时,保持右中左的顺序,出栈时保持左中右的顺序} else {//flag为true,表明该节点可以直接输出result.add(node.treeNode.val); // 加入到结果集}}return result;}

后序迭代遍历

    public static List<Integer> postOrderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNodeWithFlag> st = new Stack<>();if (root != null) st.push(new TreeNodeWithFlag(root, false));while (!st.empty()) {TreeNodeWithFlag node = st.pop();if (!node.flag) {//flag为false,下面再将中右左节点添加到栈中//令flag为true,表明下次访问到可以直接输出,再处理左右孩子node.flag=true;// 先添加中节点st.push(node);// 添加右节点(空节点不入栈)if (node.treeNode.right != null) st.push(new TreeNodeWithFlag(node.treeNode.right, false));// 添加左节点(空节点不入栈)if (node.treeNode.left != null) st.push(new TreeNodeWithFlag(node.treeNode.left, false));//最终进栈时,保持中右左的顺序,出栈时保持左右中的顺序} else {//flag为true,表明该节点可以直接输出result.add(node.treeNode.val); // 加入到结果集}}return result;}

具体例子

树的逻辑结构:

    public static void main(String[] args) {TreeNode treeNode1 = new TreeNode(null, null, 1);TreeNode treeNode2 = new TreeNode(null, null, 2);TreeNode treeNode3 = new TreeNode(null, null, 3);TreeNode treeNode4 = new TreeNode(null, null, 4);TreeNode treeNode5 = new TreeNode(null, null, 5);TreeNode treeNode6 = new TreeNode(treeNode1, null, 6);TreeNode treeNode7 = new TreeNode(treeNode3, treeNode2, 7);TreeNode treeNode8 = new TreeNode(treeNode5, treeNode4, 8);TreeNode treeNode9 = new TreeNode(treeNode7, treeNode6, 9);TreeNode treeNode10 = new TreeNode(treeNode9, treeNode8, 10);System.out.print("前序遍历:");preorderTraversal(treeNode10).forEach(System.out::print);System.out.print("\n中序遍历:");inorderTraversal(treeNode10).forEach(System.out::print);System.out.print("\n后序遍历:");postOrderTraversal(treeNode10).forEach(System.out::print);}

输出结果:

前序遍历:10973261854

中序遍历:37291610584

后序遍历:32716954810

http://www.xdnf.cn/news/9794.html

相关文章:

  • Linux轻量级文件传输——tftp命令
  • 【QQ音乐】sign签名| data参数 | AES-GCM加密 | webpack(上)
  • 腾控产品在油田间抽节能中的应用
  • Python深度学习植被参数反演AI辅助代码生成—模型构建—实战案例
  • vue3 控制url更新但不让页面更新
  • 回溯算法找出来最优价格组合
  • 深度学习-梯度消失和梯度爆炸
  • 光的干涉、衍射与偏振
  • Python入门手册:模块和包的导入与使用
  • Cookie与Session深度解析:Web会话管理的核心技术
  • 健康管理软件未来趋势:三大核心功能深度解析
  • Windows下的Qtxlsx下载和编译打包成库
  • 消息队列从入门到实战:用外卖订单理解高并发系统的核心设计
  • YOLOv8 区域计数系统:基于计算机视觉的智能物体计数方案
  • vue3+element plus 自定义组件,单列的方块 图形加文字列表
  • 写作即是生活
  • 华南版权服务大厅启用:富唯智能携具身智能人形机器人亮相,赋能版权产业生态革新
  • 【深度学习-pytorch篇】2. Activation, 多层感知机与LLaMA中的MLP实现解析
  • 数据结构与算法:数位dp
  • 在多线程编程里,若要强制两个线程按特定次序访问相同内存区域,可借助多种同步机制达成
  • Linux软链接的目的
  • 召回增强RAPTOR策略
  • 响应式布局进阶:企业商城系统复杂交互页面的多端适配方案
  • Python训练打卡Day36
  • flutter加载dll 报错问题
  • Cesium实现标注动画
  • SMME 2025:创新海洋工程模式,迎接未来挑战
  • 深入解析 CountDownLatch、Semaphore 和CyclicBarrier
  • NHANES指标推荐:CircS
  • 3D LUT--颜色魔方