#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node {datatype data;struct node *left, *right;
}BitTree;//*************************Begin*************
//添加辅助栈的结构:主要是用来保存树的节点
//简言之,这个就是用来存树节点的栈
typedef BitTree BitTreeNode;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
//-——————————修订V0.01——changed by dingst
//BitTreeNode data;那么一定是侵入式的,为什么=》逐个深拷贝每一个分量
//只要是BitTreeNode* Addr;在32位中,这个值永远是4字节
//保存的都是传入的源节点的地址值的一个副本(c/C++语言,都是指拷贝形式)
typedef struct linknode {BitTreeNode* _NodeAddrCopy;struct linknode* pNext;
}LinkStack;LinkStack* InitLkStack(void) {LinkStack* p = NULL;p = (LinkStack*)malloc(sizeof(LinkStack));if (p != NULL)p->pNext = NULL;return p;
}//建立一个带头节点的链表作为链栈BOOL IsEmptyLkMazeStack(LinkStack* top) {return (top->pNext == NULL);
}LinkStack* PushLkStack(LinkStack* top, BitTreeNode* elem) {LinkStack* p = NULL;//深拷贝的bug修订,如果传入的是空指针,那么不能访问,修订人丁宋涛//elem就是将当前树节点的地址值考一份:比如0x00d500578,我们现在//栈就是把这个0x00d500578拷贝一份下来,放到我们_NodeAddrCopy//及时栈节点出栈,此时也是我么你的_NodeAddrCopy这个变量被释放//与原先的树节点没有关系,不会影响//elem是这个函数的形参,它将调用者的实参拷贝一份放入到elem中//所以elem就是这个0x00d500578//我们用p->_NodeAddrCopy = elem;的操作//就把0x00d500578保存下来了,此时他就是原节点地址的一个副本if (elem != NULL){p = (LinkStack*)malloc(sizeof(LinkStack));//todo:我们用新建节点,对传入的二叉树节点做一个深拷贝//changed by dingst,我们要做非侵入式的设计/*p->data.data = elem->data;p->data.left = elem->left;p->data.right = elem->right;*/p->_NodeAddrCopy = elem;p->pNext = top; //p节点尾插入lkStacktop = p;//top栈顶指针上移}//我们思考,既然我们允许空指针进入到我们的栈结构,那么我们如何修订//我们的出栈?//top为空以为着?//意味着是否和栈空条件冲突了?//如果我们让我们的NULL进入栈空间,那么出栈也会造成空指针//这意味着当前的节点不动return top;
}//出栈:pData是传入的一个“临时”空间用来接收出栈的节点信息。
LinkStack* PopLkStack(LinkStack* top, BitTreeNode *pData) {LinkStack* p;if (top != NULL) {//*pData = top->data;//----changed by dingst/*pData->data = top->data.data;pData->left = top->data.left;pData->right = top->data.right;*/pData = top->_NodeAddrCopy;//pData实际上深拷贝了一份节点信息,所以释放栈中节点,不会影响//节点数据p = top;top = p->pNext;free(p);}return top;
}
//*************************End***************
//辅助队列Q,这是用来存关系的
BitTree* Q[16];//这是一个指针数组,它将缓存节点的地址,因为这个地址将以//left域,或者right域进入二叉链表,它本身不维护i,2i,2i+1的关系//他的关系通过front,rear来维护//按照直观的建立,我们首先想到的是层次法//我们根据层次,来逐一将二叉树建立//输入的数组是按照完全二叉树的编号规则设立,即//数组角标反映了节点位置关系(存联系)//逐个扫描数组,直到所有的节点都存取完毕之后,就结束//约定,0表示数组当前元素为空,最后一个节点标志是-999
BitTree* CreateBinTree(int arr[]) {int i = 1;//只要我们没有扫描完元素,那么这个二叉链表就没有完成//只要扫描,我们就malloc一个节点,然后把这个节点存入left域或者right域int front = 1, rear = 0;BitTree* root = NULL;BitTree* s;//暂存节点while (arr[i] != -999) {s = NULL;if (arr[i] != 0) {//意味着这个不是空节点,那么我们就要分配空间s = (BitTree*)malloc(sizeof(BitTree));s->data = arr[i];//存数值s->left = NULL;s->right = NULL;}//要让我们新节点入队,进入缓存,等待分配双亲的left域和right域Q[++rear] = s;if (rear == 1){root = s;}else {if (s != NULL && Q[front]) {if (rear % 2 == 0) {Q[front]->left = s;}else {Q[front]->right = s;}}if (rear % 2 == 1)front++;}i++;}return root;
}
//树遍历问题:非线性结构的输出问题:前序,中序,后序
void preorder(BitTree* t) {if (t) {//根左右printf("%d ", t->data);preorder(t->left);preorder(t->right);}
}
void NonRecrvPreOrder(BitTree* root) {LinkStack* pTop = NULL;//BitTreeNode TempNode;//用来接栈中对应的二叉树节点的//-----change by dingstBitTreeNode* pNode; //我要让pNode推进树遍历,而且pNode就是//真实地址的副本pTop = InitLkStack();//pTop = PushLkStack(pTop, root);//---change by dingstpNode = root;pTop = PushLkStack(pTop, pNode);while (!IsEmptyLkMazeStack(pTop)) {pNode = pTop->_NodeAddrCopy;//178行的这个赋值操作,就将真实的节点地址赋给了pNode这个函数的局部变量pTop = PopLkStack(pTop, pNode);if (pNode != NULL) {printf("%d ",pNode->data);//对右子树压栈pTop = PushLkStack(pTop, pNode->right);pTop = PushLkStack(pTop, pNode->left);}}}
void inorder(BitTree *t) {if (t) {inorder(t->left);printf("%d ", t->data);inorder(t->right);}
}/*
中序就是左根右,这就意味着,当我们从根节点出发,应当首先走向左孩子
,而根的左孩子,是根的左子树的根节点,因此
又必须,继续访问其左孩子,直到左孩子为空
当这个节点访问之后,按照相同的规则,访问其右子树
我们借助的栈,就应该按照左根右来压栈
对于任意节点p
1 如果其左孩子不为空,则将p入栈,然后将p的左孩子置为当前的p,然后
再对p进行相同的处理
2 若左孩子为空,则取栈顶元素进行出栈操作,访问当前栈顶节点,然后
将当前的p置为栈顶节点的右孩子
3 直到p为NULL为止
*/
void NonRecvInorder(BitTree *root) {LinkStack* pTop = NULL;BitTreeNode TempNode;//用来接收栈中的对应的二叉树的节点pTop = InitLkStack();BitTree *p = root;while (p != NULL || !IsEmptyLkMazeStack(pTop)) {while (p != NULL) {pTop = PushLkStack(pTop, p);p = p->left;}if (!IsEmptyLkMazeStack(pTop)) {//因为此时我们在输出的节点信息,就一定要从栈中//取出元素//这个是取出的元素的_NodeAddrCopy分量就是树中节点的地址//因此对这个节点使用->就可以获得真实的数据printf("%d ", pTop->_NodeAddrCopy->data);p = pTop->_NodeAddrCopy; //因为我们设计的p就是要走完root全部节点,因此//p必须获得的是树节点的真实地址pTop = PopLkStack(pTop, &TempNode);//当我们把左子树的左叶子节点走完以后,//此时栈顶一定是这个左孩子的根,所以根据左根右,这个根要出栈//----change by dingst//p = &TempNode; 因为tmpenode是bitnode的对象,//所以tmpenode的地址值可以赋值给p这个指针变量,但是这个tempnode的这个地址值//是肯定不等于真实树节点的地址值的p = p->right;//走向根的右孩子 }}
}void postorder(BitTree* t) {if (t) {//左右根postorder(t->left);postorder(t->right);printf("%d ", t->data);}
}/*
后序:左右根
根节点只有在左孩子和右孩子都访问以后才能访问
因此,对于任何一个节点,都将其入栈,如果p不存在左孩子和右孩子,直接访问
或者p存在左孩子或者右孩子,但是他的左孩子和右孩子都已经被访问过了
同样的,这个节点也可以直接访问.
如果,上述条件都不满足,就要将这个p的右孩子,左孩子依次入栈
这样就能保证,每次取栈顶元素的时候,左孩子都在右孩子之前被访问,
左右孩子访问之后,根才被访问
*/void NonRecvPostorder(BitTree* root) {BitTree* cur;//当前节点==>cur 保存的就是树节点的地址BitTree* pre = NULL;//前一次访问的节点LinkStack* pTop = NULL;BitTreeNode TempNode;//注意我们就是用tempnode来接里面的内容,他是不能参与地址比较的,他的地址一定和我们栈中的地址不一样pTop = InitLkStack();pTop = PushLkStack(pTop, root);while (!IsEmptyLkMazeStack(pTop)) {cur = pTop->_NodeAddrCopy;//拿到栈中深拷贝获得的节点//在访问中,由于我们是深拷贝树的节点,因此,pre和cur的地址不一样,我们只能比较器元素内容//小技巧:利用短路,确保程序的可靠性if ((cur->left == NULL && cur->right == NULL) ||(pre != NULL && (pre == cur->left|| pre == cur->right))) {printf("%d ", cur->data);pTop = PopLkStack(pTop, &TempNode);pre = cur;}else {if (cur->right != NULL)pTop = PushLkStack(pTop, cur->right);if (cur->left != NULL)pTop = PushLkStack(pTop, cur->left);}}
}//求叶子节点:树的遍历是一种非线性遍历,在业务编程中,从实现角度上
//用递归遍历三个方法之一,判断节点属性,就可以完成暴力搜索树
int LeafCount = 0;
void LeafCount_DLR(BitTree* t) {if (t) {if(t->left == NULL && t->right == NULL){printf("%d ", t->data);LeafCount++;}LeafCount_DLR(t->left);LeafCount_DLR(t->right);}
}//因为我们现在能够不重复,不遗漏获取每一个节点,只有前中后序3种方法,所以,我们首先先写出
//先序递归获取树的深度
int hight = 0;//它可以在多个函数之间共享
//前序是根左右,我们把每一个树从root出发的树,可以看出左右两个子树
//无论我们如何遍历,我们前进到每一个节点都是潜在的根节点。
//我们可以认为每到一个新节点,就是到了一个新树,
//如果说,我们对新节点的发现,我们就认为到了一个新层,那么我们累加
//记录就可以了
//我用level作为当前树的高度
//当我用root,0传入的时候,就意味着已经有第一层的计数起始器
//我们用打擂台的思路,用h比对,就可以完成记录
/*
找一组数中的最大值
for(){if(max < a[i]) max =a[i]}
*/
int TreeDepth_DLR(BitTree *pNode, int level) {if (pNode) {level++;if (level > hight)hight = level;//这一就意味伴随着level的不断深入,我们的hight始终获得的是树中最深的层次printf("%d ", pNode->data);//从327开始到333行,我们认为这个函数的终止条件完成了//根据我们前面的观点,递归函数本身就是答案//因此hight就应该接递归函数的返回值hight = TreeDepth_DLR(pNode->left, level);hight = TreeDepth_DLR(pNode->right, level);//因为所有节点的遍历都是跟左右,所以和当前树节点传入的同时,它当前做在的层数我们要传入}return hight;
}//根据前序、中序重建二叉树
BitTree* reConstructBinTree(int pre[], int startPre, int endPre,int in[], int startIn, int endIn) {//如果起始的下标大于结束的下标,那么就退出if (startPre > endPre || startIn > endIn) {return NULL;}//前序找到根节点BitTree* pNode = (BitTree*)malloc(sizeof(BitTree));pNode->left = NULL;pNode->right = NULL;pNode->data = pre[startPre];//前根当中的首元素一定是当前子树的根节点for (int i = startIn;i <= endIn;i++) {if (in[i] == pre[startPre]) {//i-startIn就是左子树节点的个数//i -1就是中序遍历左子树的终点pNode->left = reConstructBinTree(pre, startPre + 1, startPre + i - startIn, in, startIn, i - 1);//前序右子树的遍历起始值:startPre+i-startIn+1//中序遍历右子树的起始值:i+1,endIn(i理解成你第几次切刀)pNode->right = reConstructBinTree(pre, i - startIn + startPre + 1, endPre, in, i + 1, endIn);}}return pNode;
}//求根到所有叶子节点的路径
BitTree* Paths[10] = { 0 };void OutPutPath(){int i = 0;while (Paths[i] != NULL) {printf("%d ", Paths[i]->data);i++;}printf("\n");
}//求路径的函数
void LeavesPath(BitTree* tree, int level) {if (tree == NULL)return;Paths[level] = tree;//这句话就意味着,只要走到这里,这个子树的根就被记录下来//求路径,意味着找到叶子节点,就输出if ((tree->left == NULL) && (tree->right == NULL)) {//当我确定到已经从树根节点root走到了一个叶子节点,那么它以前所有的根节点都存在paths里面Paths[level + 1] = NULL;//通过level传递了路线信息OutPutPath();return;}LeavesPath(tree->left, level + 1);LeavesPath(tree->right, level + 1);
}//判断两个二叉树是否相等
//提问如果两个二叉树的前序遍历相等,能否说明,这两个树相等?
//答案是否定的,只有这两个树的 A前序=B前序,A中序=B中序才可以。
BOOL isEqualTree(BitTree* t1, BitTree* t2) {if (t1 == NULL && t2 == NULL)return TRUE;if (!t1 || !t2)//t1,t2一个空一个不空,肯定不相同return FALSE;if (t1->data == t2->data)return isEqualTree(t1->left, t2->left) && isEqualTree(t1->right, t2->right);elsereturn FALSE;
}//使用二叉链表建立二叉搜索树:注意,我们目前这个树不支持相同元素
BitTree* insertIntoBinSearchTree(BitTree* root, int node) {BitTree* newNode;BitTree* currentNode;BitTree* parentNode;newNode = (BitTree*)malloc(sizeof(BitTree));newNode->data = node;newNode->left = NULL;newNode->right = NULL;if (root == NULL)return newNode;else {currentNode = root;//然后我们对当前节点进行定位//注意,传入root是根节点,我们要根据根节点,按照左小右大的逻辑找到节点位置//首先通过while找到新节点的位置while (currentNode != NULL) {parentNode = currentNode;if (currentNode->data > node) {currentNode = currentNode->left;}else {currentNode = currentNode->right;}}//其次,我们将父节点和子节点做连接if (parentNode->data > node) {parentNode->left = newNode;}else {parentNode->right = newNode;}}return root;
}BitTree* create_LkBinTree(int data[], int length) {BitTree* root=NULL;int i;for (i = 0;i < length;i++) {root = insertIntoBinSearchTree(root, data[i]);}return root;
}//二叉树镜像:交换两个子树
void SwapTree(BitTree* tree) {BitTree* pTemp;pTemp = tree->left;tree->left = tree->right;tree->right = pTemp;
}void ExchangeSubTree(BitTree* tree) {if (tree == NULL)return;else {SwapTree(tree);ExchangeSubTree(tree->left);ExchangeSubTree(tree->right);}
}int main(void) {int arr[17] = { 0,6,3,8,2,5,7,9,0,0,4,0,0,0,0,10,-999};BitTree *root = CreateBinTree(arr);/*printf("递归形式的先跟遍历\n");preorder(root);printf("\n非递归形式的先跟遍历\n");NonRecrvPreOrder(root);*///NonRecvInorder(root);/*printf("递归形式的后根遍历\n");postorder(root);printf("\n非递归形式的后根遍历\n");NonRecvPostorder(root);*///******************BEGIN:树的节点与深度****************/*LeafCount_DLR(root);printf("\n当前书总共有%d个叶子节点", LeafCount);*///printf("\n当前树有%d层", TreeDepth_DLR(root, 0));给定一个二叉树,我要分别得到它的左子树的深度和右子树的深度我们用复用的思想,很容易就得到了内容注意,虽然复用的思想是好的,当时要注意全局变量的副作用要对全局变量清零//hight = 0;//printf("\n当前树的左子树有%d层\n", TreeDepth_DLR(root->left, 0));//hight = 0;//printf("\n当前树的右子树有%d层\n", TreeDepth_DLR(root->right, 0));当我们在使用递归遍历中,利用全局变量进行传递的时候函数的复用应当注意全局变量的置0//******************END 树的节点与深度****************//************Beign 二叉树重建//前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}/*int pre[] = { 1,2,4,7,3,5,6,8 };int in[] = { 4,7,2,1,5,3,8,6 };BitTree* rootNew;rootNew = reConstructBinTree(pre, 0, 7, in, 0, 7);preorder(rootNew);printf("\n");inorder(rootNew);*///*************End 二叉树重建//***********Begin 获取根节点到叶子节点//LeavesPath(root, 0);//0表示层级的传递,只要根据level延伸就能找到每一棵子树的根节点//************END获取根节点到叶子节点//***************Begin  判断两颗二叉树是否一致//如果,输入的数字序列前后不一致,那么使用二叉搜索树建立的也不是同一棵树/*int arrary[] = { 1,2,3 };BitTree* tree1 = create_LkBinTree(arrary, 3);int brr[] = { 1,2,3 };BitTree* tree2 = create_LkBinTree(brr, 3);printf("\n");inorder(tree1);printf("\n");inorder(tree2);printf("\n%d", isEqualTree(tree1, tree2));*///***************End  判断两颗二叉树是否一致//*************Begin 二叉树镜像:交换一个二叉树的左右子树int arrary[] = { 5,4,7,2,3,6,8 };BitTree* tree1 = create_LkBinTree(arrary, 7);printf("\n");inorder(tree1);ExchangeSubTree(tree1);printf("\n");inorder(tree1);//*************End 二叉树镜像:交换一个二叉树的左右子树getchar();return 0;
}

二叉树非递归遍历的经典求解相关推荐

  1. C++版二叉树非递归遍历

    C++版二叉树非递归遍历 文章目录 C++版二叉树非递归遍历 一.二叉树前序遍历 二.二叉树中序遍历 三.二叉树后序遍历 一.二叉树前序遍历 /*** Definition for a binary ...

  2. 一种二叉树非递归遍历的简单写法

    一种二叉树非递归遍历的简单写法 目录 一种二叉树非递归遍历的简单写法 先序遍历 中序遍历 后序遍历 二叉树的遍历是数据结构中非常基础的一个知识点,也是面试手撕代码环节的一个常见题目.这个问题的递归写法 ...

  3. 二叉树非递归遍历(模版)

    读完本篇内容大约花费您7分钟时间 本文主要讲解二叉树非递归遍历,由于是非递归遍历,所以需要用到栈stack,我们如果仔细考虑递归遍历的代码,就能明白非递归种栈的应用. 由于几种遍历方式只是在处理中间节 ...

  4. 二叉树非递归遍历的一点理解

    二叉树是我们必须要了解的一个数据结构,针对这个数据结构我们可以衍生出好多知识. 主要是有几种遍历方式,前序遍历,中序遍历,后续遍历,层次遍历. 下面我们根据这个图来详细的说一下这几种非递归遍历的思想与 ...

  5. 数据结构_二叉树非递归遍历

    package zz;import java.util.Stack;/*** 二叉树中的二叉搜索树,即一个节点的左子节点关键值小于这个节点,右子节点的关键值大于这个节点* * @author Admi ...

  6. 二叉树非递归遍历实现(Java)

    首先理解一下二叉树节点结构.left指向左节点,right指向右节点,val为节点中的值. class TreeNode {int val;TreeNode left;TreeNode right;p ...

  7. 更简单的非递归遍历二叉树的方法

    解决二叉树的很多问题的方案都是基于对二叉树的遍历.遍历二叉树的前序,中序,后序三大方法算是计算机科班学生必写代码了.其递归遍历是人人都能信手拈来,可是在手生时写出非递归遍历恐非易事.正因为并非易事,所 ...

  8. 对于二叉树三种非递归遍历方式的理解

    利用栈实现二叉树的先序,中序,后序遍历的非递归操作 栈是一种先进后出的数据结构,其本质应是记录作用,支撑回溯(即按原路线返回):因此,基于其的二叉树遍历操作深刻的体现了其特性: 若后续的输入和其前面的 ...

  9. 二叉树的非递归遍历(c/c++)

    由于递归算法相对于非递归算法来说效率通常都会更低,递归算法会有更多的资源需要压栈和出栈操作(不仅仅是参数,还有函数地址等)由于编译器对附加的一些栈保护机制会导致递归执行的更加低效,使用循环代替递归算法 ...

最新文章

  1. 权限表管理之更新权限表数据
  2. iOS开发之iOS程序偏好设置(Settings Bundle)的使用
  3. python输出乘法口诀-【每日一练】python输出 9*9 乘法口诀表
  4. C++井字棋游戏,DOS界面版
  5. PyQt5 技术篇-QSpinBox选值框值改变触发事件实例演示,获取QSpinBox组件的值,选值框的边界值设置方法
  6. scp构造端口_指定端口号的scp
  7. 【Python 必会技巧】判断字符串是否为字母/数字/大小写/空白字符/有效标识符/可打印字符
  8. JS 数据类型转换、创建对象
  9. android系统自动休眠代码流程,Android P 电源管理(4)待机流程
  10. ADF12C+GLASSFISH4.1配置数据源
  11. IEEE 1588 校时
  12. IIS Express 启用目录浏览
  13. 致敬科比:科比投篮数据可视化
  14. VS2017超有用秘钥(亲测)
  15. WebClient Exceeded limit on max bytes to buffer : 262144 异常解决
  16. office2007卸载不干净 无法安装office64位的版本
  17. Cozmo和Vector即将满血复活卷土重来(Digital Dream Labs收购Anki)
  18. NLP 语义匹配:经典前沿方案整理
  19. 深度学习思维导图(基于TensorFlow框架)
  20. Zephyr参考文档

热门文章

  1. 免秘登陆linux_linux普通用户免秘钥登陆操作
  2. android实现计算器功能吗,利用Android实现一个简单的计算器功能
  3. OSError: image file is truncated (28 bytes not processed)
  4. “干细胞制剂制备与质检行业标准”上海“出炉”
  5. Ubuntu创建新用户的正确姿势
  6. Charles抓包实战详解
  7. LoadRunner v12.55发布,大量新增功能,各协议得到优化改进
  8. Spring 面向切面编程
  9. Java之内存模型的基础、重排序、顺序一致性、volatile、锁、final
  10. 数据库设计Step by Step (9)——ER-to-SQL转化