-
2021-03-10 01:46:39
后序遍历:双栈法,和层次遍历(双队列)很相似,唯一区别在于层次遍历用的 是队列,后序遍历用的是栈。
public static void posOrderUnRecur1(Node head){
System.out.print("PosOrder:");
if(head != null){
Stack s1 = new Stack();
Stack s2 = new Stack();
si.push(head);
while(!s1.isEmpty()){
head = s1.pop();
s2.push(head);
if(head.left != null){
s1.push(head.left);
}
if(head.right != null){
s1.push(head.right);
}
}
while(!s2.isEmpty()){
System.out.print(s2.pop().value + " ");
}
}
System.out,println();
}
public static void posOrderUnRecur2(Node h){
Sytem.out.print("PosOrder:");
if(h != null){
Stack stack = new Stack();
stack.push(h);
Node c = null;
while(!stack.isEmpty()){
c = stack.peek();
if(c.left != null && h != c.left && h != c.right){
stack.push(c.left);
}else if(c.right != null && h != c.right){
stack.push(c.right);
}else{
System.out.print(stack.pop().value + " ");
h = c;
}
}
}
System.out.println();
}
更多相关内容 -
数据结构试验报告用先序中序建立二叉树后序遍历非递归.pdf
2021-09-27 06:38:38数据结构试验报告用先序中序建立二叉树后序遍历非递归.pdf -
探讨:C++实现链式二叉树(用非递归方式先序,中序,后序遍历二叉树)
2020-12-31 02:12:20//C++实现链式二叉树,采用非递归的方式先序,中序,后序遍历二叉树#include “stdafx.h”#include<iostream>#include<string>#include <stack>using namespace std;template<class>struct BiNode{ T data; struct... -
表达式求值(二叉树非递归后序遍历).zip
2020-09-15 13:31:45用非递归后序遍历二叉树的方式实现的表达式计算,进行了精细的表达式逻辑判断和处理,可进行加减乘除、括号、小数的计算。项目结构清晰,基本都有代码注释,可用于数据结构实验。同为学习人,能力有限,不足之处还请... -
详解二叉树后序遍历非递归实现!思路超清晰
2019-07-24 02:41:42今天复习了二叉搜索树的创建,二叉树的前、中、后序遍历递归与非递归的实现,按层遍历等等。其中较难的是二叉树的后序遍历过程 因此单独拿出来详细分析一下过程,以及在这个过程中我踩得一些坑 /** * 后序非递归...今天复习了二叉搜索树的创建,二叉树的前、中、后序遍历递归与非递归的实现,按层遍历等等。其中较难的是二叉树的后序遍历过程
因此单独拿出来详细分析一下过程,以及在这个过程中我踩得一些坑
/** * 后序非递归遍历 * @param root */ /** * 思路:首先要搞清楚什么时候才能输出根节点的值,必须等到左节点和右节点都访问完的情况才能访问根节点 * 因此,访问根节点的情况有两种: * 一、当前节点的左右子树都为null时,可以直接访问根节点 * 二、当前节点的左右子树都访问过时,则可以直接访问根节点 * 则可以设置一个标志,让他标识上一个访问的结点 * 遍历栈时,得到栈顶元素,判断他的左右子树为空或者pre标志是否等于当前结点的左右孩子的其中一个时, * 当这两种情况满足其中一种,则就可以访问当前根节点。 * * 至于为什么要判断pre标志是否等于当前结点的左右孩子的其中一个,而不是判断它是否等于当前结点的右孩子? * 原因是这样的:因为可能存在这样一种情况,当前结点只有左孩子结点,则如果去判断它是否等于当前结点的右孩子结点 * 得到的一定是false,这样当前结点永远都不会被访问,并且会陷入死循环,左孩子一直循环入栈的情况。当根结点左 * 子树访问完,如果存在右子树,则会从栈顶得到右子树结点,而此时pre标志等于根节点的左子女,因此第二个条件不成立( * 即pre不会等于根节点右子女的任何一个子女结点) * * LinkedList有一个坑:push操作实际掉的addFirst方法,而pop方法实际掉的是removeFirst方法,链表头是栈顶!!并不是链表尾 */ public static void endPrintTreeNo(TreeNode root) { if(root==null)return; LinkedList<TreeNode> stack=new LinkedList<>(); stack.push(root); TreeNode pre=null,cur=null; while(!stack.isEmpty()) { cur=stack.getFirst(); if((cur.left==null&&cur.right==null)||(pre!=null&&(pre==cur.left||pre==cur.right))){ System.out.print(cur.val+" "); stack.pop(); pre=cur; } else { if(cur.right!=null) stack.push(cur.right); if(cur.left!=null) stack.push(cur.left); } } }
-
C语言数据结构之二叉树的非递归后序遍历算法
2021-01-01 11:03:16C语言数据结构之二叉树的非递归后序遍历算法 前言: 前序、中序、后序的非递归遍历中,要数后序最为麻烦,如果只在栈中保留指向结点的指针,那是不够的,必须有一些额外的信息存放在栈中。 方法有很多,这里只举一... -
二叉树的非递归后序遍历算法实例详解
2021-01-19 17:35:06前序、中序、后序的非递归遍历中,要数后序最为麻烦,如果只在栈中保留指向结点的指针,那是不够的,必须有一些额外的信息存放在栈中。方法有很多,这里只举一种,先定义栈结点的数据结构 代码如下:typedef struct{... -
数据结构试验报告用先序中序建立二叉树后序遍历非递归.doc
2020-05-22 22:55:06数据结构实验报告 实验题目: 创建并遍历二叉树 实验目的熟悉二叉树存储结构熟悉二叉树的三种遍历方法并能用非递归的方法建立并且遍历二叉树 实验内容用先序和中序建立二叉树用后序遍历并输出二叉树要求算法非递归 ... -
二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现
2022-05-05 10:28:571)递归序,3次见面,第一次见面打印叫先序遍历,第二次见面打印叫中序遍历,第三次见面打印叫后序遍历 2)非递归实现中,先序和后序是相反的,但是中序遍历挺难理解,但是核心思想也就是先去左树,再打印头,然后再...二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现
提示:今天开始,系列二叉树的重磅基础知识和大厂高频面试题就要出炉了,咱们慢慢捋清楚!
二叉树
啥是二叉树?
就是双指针,只向下指的,无环的链表二叉树的节点:
public static class Node{ public int value; public Node left; public Node right; public Node(int v){ value = v; } }
一颗合格的二叉树,无环,往下指,只有2个指针,【多个指针就是多叉树了,比如前缀树】
叶节点的左右指针全是null
造一颗二叉树://构造一颗树,今后方便使用 public static Node generateBinaryTree(){ //树长啥样呢 // 1 // 2 3 // 4 5 6 7 Node head = new Node(1); Node n2 = new Node(2); Node n3 = new Node(3); head.left = n2; head.right = n3; Node n4 = new Node(4); Node n5 = new Node(5); n2.left = n4; n2.right = n5; Node n6 = new Node(6); Node n7 = new Node(7); n3.left = n6; n3.right = n7; return head; }
关于二叉树,咱们可有得说了,
要学二叉树的遍历,包括先序,中序,后序,按层遍历,
这些有可用递归实现,和非递归实现
后面我们还要学二叉树的序列化,反序列化
寻找二叉树的后继节点、前驱节点
树形动态规划DP:二叉树的递归套路
各种关于二叉树的知识点……
二叉树的递归先序、中序、后序遍历
先序遍历:打印顺序是头、左、右
中序遍历:打印顺序是左、头、右
后序遍历:打印顺序是左、右、头比如:
先序遍历:1 2 4 5 3 7 8
中序遍历:4 2 5 1 7 3 8
后序遍历:4 5 2 7 8 3 1三者的递归函数遍历打印函数很简单:
先打印头,然后是左树,然后是右树//先序遍历打印 public static void prePrint(Node head){ if(head == null) return;//不管是头还是叶节点,这就是递归的终止条件 //先打印头,再打印左子树,再打印右子树 System.out.print(head.value +" "); prePrint(head.left); prePrint(head.right); } //中序遍历打印 public static void inPrint(Node head){ if(head == null) return; //中序遍历,先打印左边,再打印头,再打印右边 inPrint(head.left); System.out.print(head.value +" "); inPrint(head.right); } //后序遍历打印 public static void postPrint(Node head){ if(head == null) return; //后序遍历打印,先打印左边,再打印右边,最后打印头 postPrint(head.left); postPrint(head.right); System.out.print(head.value +" "); }
测试一波:
public static void test(){ Node cur = generateBinaryTree(); prePrint(cur); System.out.println(); inPrint(cur); System.out.println(); postPrint(cur); } public static void main(String[] args) { test(); // test2(); }
看结果:
**1** 2 4 5 3 6 7 4 2 5 **1** 6 3 7 4 5 2 6 7 3 **1**
递归序:二叉树递归必定要3次见面
递归序:调用递归函数f(head)
一定要三次与head见面
递归,首先来到节点1,然后去左子树,
左子树首先见到节点2,然后去左子树,
左子树首先见到节点4,然后去左子树,见到null,返回
再次见到4,再去4的右子树,见到null,返回
再次见到4,然后返回,再次见到2,然后去2的右子树,
首次见到5,然后去5的左子树,见到null,返回
再次见到5,然后去5的右子树,见到null,返回
再次见到5,然后返回,再次见到2,
返回,再次见到1,去1的右子树,见到3
去3的左子树,见到6,去6的左子树,见到null返回再次见到6,
去6的右子树,见到7,去7的左子树,null,返回,再次见到7
去7的右子树,见到null,返回再次见到7,返回再次见到6,
再返回,再次见到1,
递归到此结束发现,每一个节点,f都会访问它3次。
先序遍历:第一次见面就打印
中序遍历:第二次见面打印
后序遍历:第三次见面打印//左神汇总一波理解这个遍历的问题 //树长啥样呢 // 1 // 2 3 // 4 5 6 7 //递归序:递归总会走这么一个顺序,每一个节点都会遇见3次: //走:1,2,4,4,4,2,5,5,5,2,1,3,6,6,6,6,7,7,7,3,1 //每一个点:首先来到头遇到第一次,然后去左边访问回来又遇见一次,然后去右边访问回来在遇见一次,3次 //第一次遇到一个点,打印它:则先序遍历 //第二次遇到一个点,打印它,则中序遍历 //第三次遇到一个点,打印它,则后续遍历 //总结:也就是先,中,后,三种遇见打印叫先中后序的遍历; public static void f(Node head){ if(head == null) return; //第一次遇见就打印的话,先序遍历 //System.out.print(head.value +" "); f(head.left); //第二次遇见打印的话,中序遍历 //System.out.print(head.value +" "); f(head.right); //第三次遇见打印的话,后序遍历 System.out.print(head.value +" "); } public static void test2(){ Node cur = generateBinaryTree(); f(cur); } public static void main(String[] args) { test(); // test2(); }
你分别注释打印的位置,
就能得到结果。
二叉树的非递归先序、中序、后序遍历
任何递归实现的代码,都能通过非递归实现
既然是一个遍历的事情
就可以让栈来实现
当节点x,出来访问时,它左子右子可以陆续压栈,由于栈能先进后出,所以压入的顺序,决定了弹出的顺序
从而达到控制x的左子和右子打印的顺序
(1)如果遇到x打印,然后先压右子,再压左子,弹出时,必定先弹出左子,再弹出右子
这不就是头、左、右吗?——先序遍历(2)如果遇到x打印,然后先压左子,再压右子,弹出时,必定先弹出右子,再弹出左子
这不就是头、右、左吗?
此时,你再逆序一下:左、右、头——后序遍历
巧了吧?(3)关于中序遍历,比较复杂,但是也就是用栈巧妙地控制进出栈的顺序
咱们这么想
我们中序遍历打印顺序是左,头,右
那么先让头别打印
A:我们先压头x,然后去x的左树操作,不断地,压头,继续去左树
然后遇到null后,返回,打印左
然后打印头,然后去右树压x,继续循环A
这样的话,打印顺序就是左、头、右
在代码中,就是控制if else条件,进入左树和右树【这玩意死记硬背,记不了算了】非递归先序遍历
自己手撕代码撕清楚
//复习(1)如果遇到x打印,然后先压右子,再压左子,弹出时,必定先弹出左子,再弹出右子 //这不就是头、左、右吗?——先序遍历 public static void unRecurrentPrePrint(Node head){ if (head == null) return; Stack<Node> stack = new Stack<>(); stack.push(head);//先压头 while (!stack.isEmpty()){ //首先打印头 Node cur = stack.pop(); System.out.print(cur.value + " "); //然后反着压,先压右边,再压左边,回头就是左右顺序出 if (cur.right != null) stack.push(cur.right); if (cur.left != null) stack.push(cur.left); } }
1 2 4 5 3 6 7
非递归后序遍历
自己想清楚思想,然后手撕代码
//复习:(2)如果遇到x打印,然后先压左子,再压右子,弹出时,必定先弹出右子,再弹出左子 //这不就是头、右、左吗? //此时,你再逆序一下:左、右、头——后序遍历 public static void unRecurrentPostPrint(Node head){ if (head == null) return; //俩栈,一个控制压入顺序,一个控制逆序 Stack<Node> inStack = new Stack<>(); Stack<Node> backStack = new Stack<>(); inStack.push(head);//先入头,再入左右--逆序放入back:头右左,输出左右头 while (!inStack.isEmpty()){ Node cur = inStack.pop(); backStack.push(cur);//逆序放 if (cur.left != null) inStack.push(cur.left);//先左 if (cur.right != null) inStack.push(cur.right);//后右--出去就是头右左,back才能是左右头 } //逆序输出 while (!backStack.isEmpty()){ System.out.print(backStack.pop().value +" ");//back才能是左右头 } }
非递归中序遍历
//这样的话,打印顺序就是左、头、右 public static void unRecurrentInPrint(Node head){ if (head == null) return; Stack<Node> stack = new Stack<>(); //只要是左边不空,去左树 while (!stack.isEmpty() || head != null){ //为啥要加head呢?? if (head != null) { //先左树 stack.push(head); head = head.left;//可能就去遇到了null }else { //如果左树是null,head是叶节点 //说明树的第一个左叶节点该打印了,然后去打印头,再去右树 head = stack.pop(); System.out.print(head.value +" "); head = head.right; } } }
测试一下:
public static void test2(){ //造树 Node head = generateBinaryTree1(); //先序 unRecurrentPrePrint(head); System.out.println(); //后序 unRecurrentPostPrint(head); System.out.println(); //中序 unRecurrentInPrint(head); } public static void main(String[] args) { // test(); test2(); }
1 2 4 5 3 6 7 先序 4 5 2 6 7 3 1 后序 4 2 5 1 6 3 7 中序
总结
提示:重要经验:
1)递归序,3次见面,第一次见面打印叫先序遍历,第二次见面打印叫中序遍历,第三次见面打印叫后序遍历
2)非递归实现中,先序和后序是相反的,但是中序遍历挺难理解,但是核心思想也就是先去左树,再打印头,然后再去右树。
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。 -
二叉树(后序遍历)非递归
2021-04-15 14:23:16#include #include /* run this program using the console pauser or add your own getch, system("pause") or input loop */struct Node {int data;struct Node *left;struct Node *right;Node(int x) {data = x;...#include
#include
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
struct Node {
int data;
struct Node *left;
struct Node *right;
Node(int x) {
data = x;
left = NULL;
right = NULL;
}
};
void PostorderVisitTree(struct Node *root)
{
if (root == NULL) return;
std::stacks;
struct Node *cur = root;
struct Node *prev = NULL;
while(cur != NULL || !s.empty()) {
while(cur != NULL) {
s.push(cur);
cur = cur->left;
}
if (!s.empty()) {
cur = s.top();
if (cur->right == NULL || cur->right == prev) {
printf("%d ", cur->data);
prev = cur;
s.pop();
cur = NULL;
} else {
cur = cur->right;
}
}
}
}
int main(int argc, char** argv)
{
Node *root = new Node(1);
root->left = new Node(3);
root->left->left = new Node(2);
root->left->right = new Node(1);
root->left->right->left = new Node(1);
root->right = new Node(-1);
root->right->left = new Node(4);
root->right->left->left = new Node(1);
root->right->left->right = new Node(2);
root->right->right = new Node(5);
root->right->right->right = new Node(2);
PostorderVisitTree(root);
return 0;
}
输出结果:
2 1 1 3 1 2 4 2 5 -1 1
-
二叉树后序遍历(递归法和迭代法(非递归法))——C++
2021-12-04 19:31:18一、二叉树后序遍历 后序遍历是先遍历左子节点left,在遍历右子节点right,最后遍历父节点parent,即遍历顺序:1.2 迭代法 由于后续遍历如果按照正常迭代思路去实现将不好理解和实现,仔细观察下面前序和后续遍历... -
Java数据结构系列之——树(五):二叉树的后序遍历的递归与非递归实现
2021-04-15 14:24:10Java数据结构系列之——树(5):二叉树的后序遍历的递归与.../*** 二叉树后序遍历的递归与非递归实现** @author wl**/public class BitreePostOrder {// 后序遍历的递归实现public static void biTreePostOrderByRec... -
后序遍历二叉树非递归算法的推导及形式化证明
2017-11-27 21:36:41后序遍历二叉树非递归算法的推导及形式化证明,难得的期刊论文资料,对研究二叉树的非递归性遍历有很大帮助 -
二叉树的前序、中序、后序遍历的递归与非递归Java实现
2020-11-02 21:44:46递归的写法相对简单,但有时候非递归的实现,却需要想一想。今天,统一思考了一下,并用Java进行了实现,记录在这里,以便脑子宕机的时候看看。 一切的递归,都可以用自己写的栈来实现。 二叉树节点类: /** * ... -
自己编写的实验二叉树的后序遍历非递归算法c语言实现
2013-08-13 14:37:23自己编写的实验二叉树的后序遍历非递归算法 包括以递归中序遍历建立二叉树 前序,中序,后序递归以及非递归实现二叉树的遍历 经vc6.0编译通过 自己实验,不足之处应该很多,望指出 -
二叉树:后序遍历非递归算法
2021-11-15 00:19:52分析:后序遍历是三种遍历中最难的一种,后序遍历的特点为左右根,并且也需要借助一个栈来完成,如图,虚线表示p,q最开始的位置,用r指向最近访问过的结点。首先从根节点开始,沿着根的左孩子,将左孩子依次进行入栈... -
二叉树的基本操作:二叉树的后序遍历非递归算法
2020-10-15 21:31:42二叉树的后序遍历非递归算法 关于 (1)如何利用前序遍历创建二叉树 (2)二叉树的前序、中序、后序递归遍历 (3)二叉树的前序、中序非递归遍历 都已经在之前两篇文章中说过了 利用前序遍历创建二叉树,二叉树的... -
python 二叉树后序遍历 非递归
2018-08-18 21:19:28今天拼多多面试,面试官让我写非递归的二叉树后序遍历,听到这个题目我就感觉不妙,根本不记得了啊,唯一记得的是后序是最难的,还有,需要用栈。 没办法,只能先硬着头皮试试,一开始大脑跟纸一样空白,后来突然... -
c语言实现二叉树的前中后序遍历 递归和非递归 数据结构
2014-03-23 20:51:351.输入前序和中序遍历结果,建立二叉树 2.实现二叉树的三种递归遍历算方法 3.实现二叉树的三种非递归遍历算法 4.实现二叉树的旋转90°后的打印,直观树形结构 -
python3实现二叉树的遍历与递归算法解析(小结)
2020-12-31 16:11:17二叉树有三种遍历方式:先序遍历,中序遍历,后续遍历 即:先中后指的是访问根节点的顺序 eg:先序 根左右 中序 左根右 后序 左右根 遍历总体思路:将树分成最小的子树,然后按照顺序输出 1.1 先序遍历 a 先访问根... -
Java 二叉树后序遍历(递归/非递归)
2020-05-27 10:21:34Java 二叉树后序遍历(递归/非递归) -
链式二叉树的后序创建、递归后序遍历、非递归堆栈后序遍历、后序销毁
2014-06-13 09:08:53链式二叉树的后序创建、递归后序遍历、非递归堆栈后序遍历、后序销毁 -
C语言非递归后序遍历二叉树
2020-08-29 01:45:58主要为大家详细介绍了C语言非递归后序遍历二叉树,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 -
Python 二叉树先序中序后序遍历 非递归
2020-02-21 16:55:04先序遍历二叉树非递归 类似递归的思想,遇到一个节点先打印出来,然后依次访问左右节点。但是非递归借助栈来实现有所不同,应该先打印当前节点,然后依次入栈右节点和左节点,因为此时栈的插入顺序和弹出顺序相反... -
C++实现二叉树链表存储结构先中后序遍历的递归算法和非递归算法和顺序存储完全二叉树
2020-11-21 04:02:44(1)假设二叉树采用链接存储方式存储,分别编写一个二叉树先序遍历的递归算法和非递归算法。 (2)一棵完全二叉树以顺序方式存储,设计一个递归算法,对该完全二叉树进 行中序遍历。 3.解题思路 (1)采用... -
二叉树后序遍历(递归+非递归)Java
2019-10-25 08:39:43后序遍历(递归)代码图解功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个... -
二叉树的前序遍历、中序遍历、后序遍历(递归+非递归实现)
2022-04-01 22:54:12解决二叉树问题的核心思想是递归,在初次接触到二叉树这种数据结构时,它的递归方式遍历很容易理解,但当要求以非递归方式来实现遍历时,就显得手足无措了,本篇博客以递归和非递归两种方式实现二叉树的遍历. ... -
二叉树后序遍历非递归算法(详解)
2018-08-10 11:50:54前序和中序都比较简单 后序我按自己的理解好好说一下吧,基本思路是利用堆栈将递归转化为循环 首先定义节点结构 struct BTNode { int val; bool visR; bool visL; BTNode *LeftChild, *RightChild; BTNode...