二叉树学习之非递归遍历

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

  先上代码:

 

#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" {
#endiftypedef 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));
#endifvoid 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
endififeq (y, $(debug))CFLAGS += -g
endifCC = gcc
CPLUS = g++
CFLAGS += -Wall
TARGET = treeall:$(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

推荐阅读:https://blog.csdn.net/LLZK_/article/details/52829525

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/508693.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二叉树学习之堆排序

认识堆是从堆排序开始的 二叉堆是完全二叉树或者是近似完全二叉树,堆存储在数组中: 根结点下标为0时,下标为n的元素的子结点下标分别为2*n1,2*n2,其父结点下标为(n-1)/2 二叉堆的特性: 1、父结点的键值总是>(<)任何一个子结点的键值 2、每个结点的左右子树都是二叉堆…

步入github世界

在源码的世界里&#xff0c;越来越多的优秀源码涌现&#xff0c;开源的世界不但代表他的优秀&#xff0c;也代表了他优秀的传播途径。 https://github.com/ github自从2008年现世&#xff0c;可谓是后来者居上。开源代码的公开库&#xff0c;优秀程序员的博客园&#xff0c;热心…

libevent学习__学习历程总结

The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. Furthermore, libevent also support callbacks due to signals or regular timeouts.环境搭建 下载: http:…

redis☞ python客户端

安装 https://pypi.python.org/pypi/redis/ https://github.com/andymccurdy/redis-py 参照官网,安装命令 sudo pip install redis 或者 sudo easy_install redis 亦或 源码包执行sudo python setup.py install实例 >>> import redis >>> r redis.Redis(ho…

Catalan数应用

Catalan数应用 Catalan数应用原理卡特兰数经典应用括号化买票找零组合数与阶乘计算 卡特兰数又称卡塔兰数&#xff0c;是组合数学中一个常出现在各种计数问题中的数列。由以比利时的数学家欧仁查理卡塔兰 (1814–1894)命名。 其前几项为 : 1, 2, 5, 14, 42, 132, 429, 1430,…

统计整数n的二进制表示中1的个数

(1)逐位判断&#xff08;位运算&#xff09;int counter_ones(unsigned n){int counter 0;While (n) {counter n&1;n >>1;}return counter;}(2)一个整型不为0&#xff0c;那么二进制表示时&#xff0c;至少包含一位1。如果整数减去1&#xff0c;那么最右边的1变成0…

百度2015校园招聘软件开发笔试题及答案

简单题&#xff08;本题共30分&#xff09; 请简述Tcp-ip的3次握手以及4次挥手过程&#xff1f;并解释为何关闭连接需要4次挥手(10分) 详细答案参见TCP/IP协议三次握手与四次握手流程解析 TCP三次握手、四次挥手过程如下: 通常情况下&#xff0c;一个正常的TCP连接&#xf…

python常见数据结构

Python中常见的数据结构可以统称为容器&#xff08;container&#xff09;。序列&#xff08;如列表和元组&#xff09;、映射&#xff08;如字典&#xff09;以及集合&#xff08;set&#xff09;是三类主要的容器。 一、序列&#xff08;列表、元组和字符串&#xff09; 序列…

理解‘*‘,‘*args‘,‘**‘,‘**kwargs‘

原文Understanding ‘*’, ‘*args’,’**‘and’**kwargs’ 刚开始学习python的时候&#xff0c;对有关args,kwargs,*和**的使用感到很困惑。相信对此感到疑惑的人也有很多。我打算通过这个帖子来排解这个疑惑(希望能减少疑惑)。 让我们通过以下5步来理解&#xff1a; 通过…

MongoDb随笔,PyMongo简单使用

安装MongoDb 【更新2021-07-06】 https://www.mongodb.com/try/download/community 下载对应系统的软件版本&#xff08;CentOS7.9 mongod 4.4.6&#xff09;rpm -ivh mongodb-org-server-4.4.6-1.el7.x86_64.rpm安装服务systemctl start mongod启动服务rpm -ivh mongodb-org…

python中classmethod与staticmethod的差异及应用

类中三种函数的应用 #!/usr/bin/env python # -*- coding: utf-8 -*-class TClassStatic(object):def __init__(self, data):self.data datadef printself(*arg):# for item in arg:# print item.dataprint("printself: ", arg)staticmethoddef smethod(*arg):prin…

python3元类简介(metaclass)

在Python中可以用内置函数type查看对象的类型&#xff0c;isinstance查看某个对象是某个类实例&#xff0c;通过type可以实现动态类&#xff0c;以及通过metaclass实现动态类 type()与isinstance()判断对象类型 Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:54:40) [MSC v…

linux ps 命令使用

Linux中的ps命令是Process Status的缩写。ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照&#xff0c;就是执行ps命令的那个时刻的那些进程&#xff0c;如果想要动态的显示进程信息&#xff0c;就可以使用top命令。 linux上进程有5种状态 ps命令使…

UML序列图总结

序列图主要用于展示对象之间交互的顺序。 序列图将交互关系表示为一个二维图。纵向是时间轴&#xff0c;时间沿竖线向下延伸。横向轴代表了在协作中各独立对象的类元角色。类元角色用生命线表示。当对象存在时&#xff0c;角色用一条虚线表示&#xff0c;当对象的过程处于激活…

UML用例图总结

用例图主要用来描述 用户、需求、系统功能单元 之间的关系。它展示了一个外部用户能够观察到的系统功能模型图。 【用途】&#xff1a;帮助开发团队以一种可视化的方式理解系统的功能需求。 用例图所包含的元素如下&#xff1a; 1. 参与者(Actor) 表示与您的应用程序或…

动态规划--换零钱

题目描述 想兑换100元钱&#xff0c;有1,2,5,10四种钱&#xff0c;问总共有多少兑换方法 递归解法 #include<iostream> using namespace std; const int N 100; int dimes[] {1, 2, 5, 10}; int arr[N1] {1}; int coinExchangeRecursion(int n, int m) //递归…

学生表 课程表 成绩表 教师表常用SQL语句

学生表 课程表 成绩表 教师表 50个常用sql语句 建表 Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,Tname) 教师表 ---- If database exists the same name datatable deletes it. IF EXISTS(SELECT TABLE_NAME FRO…

Linux网络编程常见面试题

概述 TCP和UDP是网络体系结构TCP/IP模型中传输层一层中的两个不同的通信协议。 TCP&#xff1a;传输控制协议&#xff0c;一种面向连接的协议&#xff0c;给用户进程提供可靠的全双工的字节流&#xff0c;TCP套接口是字节流套接口(stream socket)的一种。UDP&#xff1a;用户…

sizeof与offsetof有关的结构体详解

sizeof与offsetof在程序中经常遇到&#xff0c;但在面试中其应用使得许多小伙伴吃闭门羹&#xff0c;被面试官问得哑口无言。接下来对两者的应用做详细介绍。 关于sizeof 定义 sizeof乃C/C中的一个操作符(operator), 简单的说其作用就是返回一个对象或者类型所占的内存字节数…

linux线程间同步(1)读写锁

读写锁比mutex有更高的适用性&#xff0c;可以多个线程同时占用读模式的读写锁&#xff0c;但是只能一个线程占用写模式的读写锁。 1. 当读写锁是写加锁状态时&#xff0c;在这个锁被解锁之前&#xff0c;所有试图对这个锁加锁的线程都会被阻塞&#xff1b; 2. 当读写锁在读加…