首页 > 代码库 > 二叉树学习之非递归遍历
二叉树学习之非递归遍历
二叉树递归遍历可谓是学过数据结构的同仁都能想一下就能写出来,但在应聘过程我们常常遇到的是写出一个二叉树非递归遍历函数,接着上篇文章写二叉树的非递归遍历,先难后易,一步一步的来.
先上代码:
#include "binarytree.h" #include <stack> #include <queue> #ifndef RECU #warning("RECU is not defined") /** *前序遍历(根左右) * *1、当前节点为非空,访问当前节点,压栈其右子节点,考虑其左子节点 *2、当前节点为NULL,出栈 * *@param t to visit *@param visit point to a func */ void pre_order(link t, void (*visit)(link)) { std::stack<link> myStack; while( t || !myStack.empty() ) { if ( t ) { visit(t); myStack.push(t->rchild); t = t->lchild; } else { t = myStack.top(); myStack.pop(); } } } /** *中序序遍历(左根右) * *1、当前节点为非空,在访问当前节点前要先访问其左子节点, * 压栈当前节点,判断其左子结点,一直压栈左子节点 *2、当前节点为NULL,出栈访问,其左子结点比当前节点出栈访问早, * 此时当前节点是其右节点的父节点的角色,考虑其右节点 * *在遍历过程中角色转换很重要 * *@param t to visit *@param visit point to a func */ void in_order(link t, void (*visit)(link)) { std::stack<link> myStack; while( t || !myStack.empty() ) { if ( t ) { myStack.push(t); t = t->lchild; } else { t = myStack.top(); myStack.pop(); visit(t); t = t->rchild; } } } /** *后序遍历(左右根) * *1、由于在访问当前树的根结点时,应先访问其左、右子树,因而先将根结点入栈, * 接着将右子树也入栈,然后考虑左子树,重复这一过程直到某一左子树为空 *2、如果当前考虑的子树为空, * 1.若栈顶不为空,说明第二栈顶对应的树的右子树未处理, * 则弹出栈顶,下次循环处理,并将一空指针入栈以表示其另一子树已做处理; * 2.若栈顶也为空树,说明第二栈顶对应的树的左右子树或者为空,或者均已做处理, * 直接访问第二栈顶的结点,访问完结点后,若栈仍为非空,说明整棵树尚未遍历完, * 则弹出栈顶,并入栈一空指针表示第二栈顶的子树之一已被处理。 * *@param t to visit *@param visit point to a func */ void post_order(link t, void (*visit)(link)) { std::stack<link> myStack; while( 1 ) { if ( t ) { myStack.push(t); myStack.push(t->rchild); t = t->lchild; } else { t = myStack.top(); myStack.pop(); if (!t) { t = myStack.top(); myStack.pop(); visit(t); if (myStack.empty()) break; t = myStack.top(); myStack.pop(); } myStack.push(NULL); } } } #endif /** *层遍历 * *@param t to visit *@param visit point to a func */ void level_order(link t, void (*visit)(link)) { std::queue<link> myQueue; if (t) { myQueue.push(t); while( !myQueue.empty() ) { link tmp = myQueue.front(); myQueue.pop(); visit(tmp); if (tmp->lchild != NULL) myQueue.push(tmp->lchild); if (tmp->rchild != NULL) myQueue.push(tmp->rchild); } } }在非递归遍历函数中我们用到了堆栈和队列,为几种注意力到一件事上,在堆栈和队列的实现上,本人第一时间想到的是拿来主义,到github去下载别人的源码来实现.
下载了一个版本都没达到想要的效果,于是乎目标转移到C++ STL上,最终版本是在C源文件上实现二叉树的递归函数,在CPP文件中实现二叉树的非递归函数.所以在make的时候定义一个变量recu=y来应用递归函数,其他情况则应用非递归函数。
由于C与C++公用,那么咱们的头文件就要动点手脚了,否就会有意外情况出现
/* binarytree.h */ #ifndef BINARYTREE_H #define BINARYTREE_H #ifdef __cplusplus extern "C" { #endif typedef struct node *link; /** *节点中的数据类型重定义 */ typedef unsigned char TElemType; struct node { TElemType item; link lchild, rchild; }; link init(TElemType VLR[], TElemType LVR[], int n); void pre_order(link t, void (*visit)(link)); void in_order(link t, void (*visit)(link)); void post_order(link t, void (*visit)(link)); #ifndef RECU void level_order(link t, void (*visit)(link)); #endif void pprint(link t); int count(link t); int depth(link t); void destroy(link t); /** *http://www.cnblogs.com/bizhu/archive/2012/08/19/2646328.html 算法图解 * *二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树, *它或者是一棵空树;或者是具有下列性质的二叉树: *(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值; *(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值; *(3)左、右子树也分别为二叉排序树; *(4)排序二叉树的中序遍历结果是从小到大排列的. * *二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低,为O(log n)。 *二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。 * *搜索,插入,删除的复杂度等于树高,期望O(log n),最坏O(n)(数列有序,树退化成线性表) *改进版的二叉查找树可以使树高为O(logn),如SBT,AVL,红黑树等. * *程序来源于Linux C编程一站式学习 */ link bstSearch(link t, TElemType key); link bstInsert(link t, TElemType key); link bstDelete(link t, TElemType key); /** *http://baike.baidu.com/view/593144.htm?fr=aladdin *平衡二叉树 */ #ifdef __cplusplus } #endif #endif对于__cplusplus这个玩意纠结了很久,模模糊糊知道他是干什么用的,具体放在什么地方纠结了好一阵子,最后一狠心自己动手编译试一下,暂且只定义响应的空函数,看看编译连接是否OK,出人意料万事OK。由此看来动手能力决定一切啊。
对Makefile文件改动如下:
#if you want to use recursive func,please make recu=y ifeq (y, $(recu)) CFLAGS += -DRECU endif ifeq (y, $(debug)) CFLAGS += -g endif CC = gcc CPLUS = g++ CFLAGS += -Wall TARGET = tree all:$(TARGET) .c.o: $(CC) $(CFLAGS) -o $@ -c $< .cpp.o: $(CPLUS) $(CFLAGS) -o $@ -c $< $(TARGET): non_binarytree.o binarytree.o main.o $(CPLUS) $(CFLAGS) -o $@ $^ test: @./tree > result.txt && python result.py | tree -b2 .PHONY: all clean clean: $(RM) $(TARGET) *.o在改动Makefile的工程中遇到了两个问题:
1、本人认为主程序是C程序,所以在连接的时候用的是gcc,随后爆出"undefinedreference to ‘__gxx_personality_v0‘ " 错误。
重来没有遇到过,只有问度娘,得出的结果如下:
对于 C++ 程序,编译的时候用 gcc 或者 g++ 都可以。但是在进行连接的时候最好用 g++,因为用 g++ 会自动进行 C++ 标准库的连接;用 gcc 连接 C++ 程序也可以,但是需要人为指定连接 C++ 标准库,否则就会出现 undefined reference to `__gxx_personality_v/0‘ 之类的错误。可见-lstdc++ 所对应的是标准C++库
2、当定义RECU这个宏后,发现函数重复定义,排查了一下函数定义,在C文件中函数定义用"#dedef RECU **** #endif",在C++文件中"#ifndef RECU ...... #endif"隔开了呀,用"#warning()"添加编译过程中的打印信息,定义RECU与否总会编译到非递归函数,我去,奇了怪了。
在仔细排查,发现编译C++文件时没有定义RECU。
Makefile内容改动如下:
%.o:%.c $(CC) $(CFLAGS) -o $@ -c $< %.o:%.cpp $(CPLUS) $(CFLAGS) -o $@ -c $< ================改成了================ .c.o: $(CC) $(CFLAGS) -o $@ -c $< .cpp.o: $(CPLUS) $(CFLAGS) -o $@ -c $<问题是解决了,但这两种写法有什么不同还是没有个所以然
======================================================================
脚本是自动化的神器,输出的序列手动用tree工具转换成图形确实麻烦,于是乎想到之前有个博友测试计算公式的效率时用到python:
1、python输出计算所需要的参数,print函数就OK
2、测试程序用scanf判断输入的参数个数是否OK
3、实现就是python程序输出的结构通过管道送到测试程序
自动化测试用这个方法可能凑效
写个脚本一行行的输出测试程序的结果:
fp = open('result.txt') for line in fp.readlines(): print(line)Makefile添加如下语句:
test: @./tree > result.txt && python result.py | tree -b2
测试只需make test就OK
二叉树学习之非递归遍历
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。