二叉树学习之非递归遍历

二叉树递归遍历可谓是学过数据结构的同仁都能想一下就能写出来,但在应聘过程我们常常遇到的是写出一个二叉树非递归遍历函数,接着上篇文章写二叉树的非递归遍历,先难后易,一步一步的来.

先上代码:

#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 [email protected] -c $<

.cpp.o:
	$(CPLUS) $(CFLAGS) -o [email protected] -c $<

$(TARGET): non_binarytree.o binarytree.o main.o
	$(CPLUS) $(CFLAGS) -o [email protected] $^

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 [email protected] -c $<

%.o:%.cpp
	$(CPLUS) $(CFLAGS) -o [email protected] -c $<
================改成了================
.c.o:
	$(CC) $(CFLAGS) -o [email protected] -c $<

.cpp.o:
	$(CPLUS) $(CFLAGS) -o [email protected] -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

时间: 2024-08-01 10:42:30

二叉树学习之非递归遍历的相关文章

二叉树后序非递归遍历

package com.basic.bt; import java.util.ArrayList; import java.util.Stack; /** * Created by mac on 2017/1/19. */ public class PostOrderBT { ArrayList<Integer> result = new ArrayList<Integer>(); public void postorder(TreeNode root) { if(root ==

二叉树中序非递归遍历

package com.basic.bt; import java.util.ArrayList; import java.util.Stack; /** * Created by mac on 2017/1/19. */ public class InOrderBT { ArrayList<Integer> result = new ArrayList<Integer>(); ArrayList<Integer> traversal = new ArrayList&l

史上最简明易懂非递归遍历二叉树算法

巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 遍历二叉树的递归函数是体现了算法之美的高妙算法,思路清晰,代码简洁,读之赏心悦目.代码例如以下: 程序代码: void PreOrderTraverse_R(BiTree BT)//採用递归方式先序遍历二叉树BT { if(BT != NULL) { printf("%c", BT->data);//输出该结点(根结点) PreOrderTraverse_R(BT->lchi

数据结构二叉树的递归与非递归遍历之 实现可编译(1)java

前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序遍历为例进行说明,中序遍历和后序遍历,以此类推! 二叉树递归与非递归遍历的区别,虽然递归遍历,跟容易读懂,代码量少,运算快,但是却容易出现溢出的问题,所以所以非递归遍历,在处理千万级的运算量时会先的很有用处. 二叉树的先序遍历:先访问根节点,再访问先后访问左右节点.如图: 二叉树的递归遍历之java

数据结构之二叉树篇卷三 -- 二叉树非递归遍历(With Java)

Nonrecursive Traversal of Binary Tree First I wanna talk about why should we use <code>Stack</code> to implement this algorithm. I think it is due to the FILO feature of Stack, and that really matters and makes sense when you get around with t

二叉树的非递归遍历--京东2015笔试回忆

题目回忆: C/C++研发试卷:偏重于数据结构的考察,编程题有2题+1题附加题: 1.输入整数n,求m,m>9,m中各个数位的乘积=n的最小整数;如n=36,m=49; 2.二叉树前序遍历的非递归实现(本文的总结) 3.求第n个数,这个序列满足(2^i)*(3^j)*(5^k),前7个为:2,3,4,5,6,8,10 .... 小题有基本的数据结构.程序运行结果.SQL题目. 4.删除表格用DROP命令,死锁产生的条件: 4.1互斥使用(资源独占) 一个资源每次只能给一个进程使用 4.2.不可强

数据结构——二叉树遍历之“递归与非递归遍历”

简述 二叉树的遍历分为先序遍历.中序遍历和后序遍历.如下图所示: 递归遍历 private void bianli1(List<Integer> list, TreeNode root) { // 先序遍历 if (root == null) { return; } list.add(root.val); bianli1(list, root.left); bianli1(list, root.right); } private void bianli2(List<Integer>

JAVA递归、非递归遍历二叉树(转)

原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { private char date; private BinTree lchild; private BinTree rchild; public BinTree(char c) { date = c; } // 先序遍历递归 public static void preOrder(BinTree t) {

【算法导论】二叉树的前中后序非递归遍历实现

二叉树的递归遍历实现起来比较简单,而且代码简洁:而非递归遍历则不那么简单,我们需要利用另一种数据结构---栈来实现.二叉树的遍历又可以分为前序.中序和后序三种,它们是按照根结点在遍历时的位置划分的,前序遍历则根结点先被遍历,中序则根结点在左右叶子节点之间被遍历,后序则是根结点最后被遍历.三种非递归遍历中,前序和中序都不是太复制,而后序遍历则相对较难. 一.前序遍历 我们这里前序遍历按照"根-左-右"的顺序来遍历.这里按照"递归--非递归"的次序来研究,之后的几种亦是