二叉树创建及遍历——C++实现,用类封装

前言:
        这个主要是数据结构实验课的作业,实验的要求如下:

         原本我想着这种东西网上搜搜拉到了,但是自己写的时候却出现了不少的问题,因此我决定自己来写一个不会出错的二叉树。

原理:

        二叉树的构成其实并不复杂,二叉树的操作也和二叉树的构成差不多,主要要对递归理解的比较深刻就可以轻松地写出二叉树(暂时不论细节)。他的节点和链表的节点差不大多:

一个data域用于数据的存储。

两个指针域,一个指向左子节点(lchild),一个指向右子节点(lchild)。

因此节点的struct构成如下:

typedef struct Node
{
	char data;
	Node* lchild, * rchild;
}*Broot,*Bnode,node; 
//*Broot是因为后面操作经常需要用到根节点,而创建的根节点是个指针,为了方便创建*Broot
//*Bnode同理,后面许多节点是直接用指针表示的。node则是全小写,比较方便后面码代码

然后我们简单的设想一下一棵树需要有的属性和操作:

1.属性:一棵树至少要有一个根吧,至少要能知道这树多少个节点吧。

2.操作:先序,中序,后序遍历;初始化;销毁;拷贝构造;用值去填充树(creat)······

因此抽象出BinaryTree类:

typedef struct Node
{
	char data;
	Node* lchild, * rchild;
}*Broot,*Bnode,node;

class BinaryTree {
private:
	int count;
public:
	Broot root; //这地方把root作为共有属性,是因为后头经常要用到,怕麻烦
	BinaryTree() :count(0), root(nullptr) {};
	bool isEmptytree() { return root; }
	void creatTree(Broot& p);//创建有值的二叉树,若子节点为空,用#表示
	void prePrint(Bnode p);//先序遍历
	void midPrint(Bnode p);//中序遍历
	void posPrint(Bnode p);//后序遍历
	void midPrint_useNoRecursion(Bnode p);//这个是题目要求的不递归(手写栈)实现中序遍历
};

P.S. 这边我们规定二叉树中没有值的的节点用#表示

实现:

        这里我们需要先达成一个共识,我们在初始化这棵树的时候其实这棵树的根是个虚拟的,因为我们并没有往里头存上数据(树的根节点也是存数据的)。因此,我们的构造函数只需要将根指针置空就好,以防后续的未初始化问题。

BinaryTree() :count(0), root(nullptr) {};

        判空操作:我们首先要知道空树的定义。空树是没有任何一个节点的树,因此他也不存在根节点,所以我们只需要判断根节点是否存在即可。

bool isEmptytree() { return root; }

        递归先序,中序,后序遍历:为啥先说遍历,因为递归的遍历真的很简单···我们首先理解这三种遍历的区别:只是输出数据的位置不同:先序就是先输出,再往下递归左子树,右子树;中序就是先递归左子树,递归出来以后输出,再递归右子树;后续同理。

        然后,在什么情况下是要输出的呢?显然是节点存在就输出。因此三种递归遍历实现如下:

void BinaryTree::prePrint(Bnode p)
{
	if (p == NULL)return;
	cout << p->data;
	prePrint(p->lchild);
	prePrint(p->rchild);
}
void BinaryTree::midPrint(Bnode p)
{
	if (p == NULL)return;
	midPrint(p->lchild);
	cout << p->data;
	midPrint(p->rchild);
}
void BinaryTree::posPrint(Bnode p)
{
	if (p == NULL)return;
	posPrint(p->lchild);
	posPrint(p->rchild);
	cout << p->data;
}

P.S. 这边其实也会出问题,哪边呢,复制的时候复制快了,会写成下头这个错样子 /doge

这个是错的,别搞错了
void BinaryTree::midPrint(Bnode p)
{
	if (p == NULL)return;
	prePrint(p->lchild);
	cout << p->data;
	prePrint(p->rchild);
}

然后是用值去填充树

        这里其实涉及了一个比较有意思的问题:关于二重指针的传址。本文不做过多讨论。我们选择使用一重指针的引用来规避这个比较繁琐的问题。        

        为什么要传一重指针的引用(这个Broot是一重指针的别名)?因为我们的root是用指针写的,我们需要实际的给这个root创建一个地址。

        我们用getchar()函数去一个个的读我们传入的数据,每次递归读一个,读到#终止本次读入。总体上是先向左存,再向右存。实现如下:

void  BinaryTree::creatTree(Broot& p) {
	char c;
	c = getchar();
	if (c == '#') {
		p = NULL;
	}
	else {
		p = new node;
		p->lchild = NULL;
		p->rchild = NULL;
		p->data = c;
		creatTree(p->lchild);
		creatTree(p->rchild);
	}
}

        最后,我们实现最麻烦的用非递归实现的中序遍历。为什么说他麻烦呢,因为他要在函数体里手写一个栈用来存储遍历的顺序,以便回溯。

        但是好在我们需要的栈不需要很强大的功能,因此我们用极其简单的顺序栈来实现就足够了,这个顺序栈的每个单元存的应该都是树中遍历过得节点的地址,这样才能实现回溯效果。为了在主体中不输出,我们把中序遍历中输出的功能放到栈的pop中。

 为什么是pop里呢?因为中序遍历是在每一个lchild到头以后,退出回到上一层的时候进行的输出,也就是在退栈的时候才进行输出。栈的结构如下:

//这里用struct来写,是因为他的权限默认全是public
struct stack_ptr {
		Node* arr[20] = { NULL };
		int top = 0;
		void push(Node* p) {
			arr[top++] = p;
		}
		Node* pop() {
			cout << arr[top - 1]->data;
			top--;
			return arr[top];
		}
	};
stack_ptr s;

        主体的遍历部分逻辑是这样的:树节点存在或者栈不空的情况下,我们依次把左子树入栈,左子树到头以后退栈,指针回溯,遍历右子树,重复这个过程,直到全部遍历完成(栈为空)。这个地方需要注意的点就是总体的循环条件里头的s.top!=0;

非递归实现的中序遍历代码如下:

void BinaryTree::midPrint_useNoRecursion(Bnode p)
{
	struct stack_ptr {
		Node* arr[20] = { NULL };
		int top = 0;
		void push(Node* p) {
			arr[top++] = p;
		}
		Node* pop() {
			cout << arr[top - 1]->data;
			top--;
			return arr[top];
		}
	};
	stack_ptr s;
	while (p != NULL || s.top != 0) {
		while (p != NULL) { s.push(p); p = p->lchild; };
		if (s.top != s.base) {
			p = s.pop();
			p = p->rchild;
		}
	}
}

自此实现完成,下面放上总览以及测试用例

类实现总览:

typedef struct Node
{
	char data;
	Node* lchild, * rchild;
}*Broot,*Bnode,node;

class BinaryTree {
private:
	int count;
public:
	Broot root;
	BinaryTree() :count(0), root(nullptr) {};
	bool isEmptytree() { return root; }
	void creatTree(Broot& p);//创建有值的二叉树,若子节点为空,用#表示
	void prePrint(Bnode p);
	void midPrint(Bnode p);
	void posPrint(Bnode p);
	void midPrint_useNoRecursion(Bnode p);
};
void  BinaryTree::creatTree(Broot& p) {
	char c;
	c = getchar();
	if (c == '#') {
		p = NULL;
	}
	else {
		p = new node;
		p->lchild = NULL;
		p->rchild = NULL;
		p->data = c;
		creatTree(p->lchild);
		creatTree(p->rchild);
	}
}
void BinaryTree::prePrint(Bnode p)
{
	if (p == NULL)return;
	cout << p->data;
	prePrint(p->lchild);
	prePrint(p->rchild);
}
void BinaryTree::midPrint(Bnode p)
{
	if (p == NULL)return;
	midPrint(p->lchild);
	cout << p->data;
	midPrint(p->rchild);
}
void BinaryTree::posPrint(Bnode p)
{
	if (p == NULL)return;
	posPrint(p->lchild);
	posPrint(p->rchild);
	cout << p->data;
}
void BinaryTree::midPrint_useNoRecursion(Bnode p)
{
	struct stack_ptr {
		Node* arr[20] = { NULL };
		int base = 0;
		int top = 0;
		void push(Node* p) {
			arr[top++] = p;
		}
		Node* pop() {
			cout << arr[top - 1]->data;
			top--;
			return arr[top];
		}
	};
	stack_ptr s;
	while (p != NULL || s.top != 0) {
		while (p != NULL) { s.push(p); p = p->lchild; };
		if (s.top != s.base) {
			p = s.pop();
			p = p->rchild;
		}
	}
}

测试数据:

 整体代码:

#include<iostream>
using namespace std;

typedef struct Node
{
	char data;
	Node* lchild, * rchild;
}*Broot,*Bnode,node;

class BinaryTree {
private:
	int count;
public:
	Broot root;
	BinaryTree() :count(0), root(nullptr) {};
	bool isEmptytree() { return root; }
	void creatTree(Broot& p);//创建有值的二叉树,若子节点为空,用#表示
	void prePrint(Bnode p);
	void midPrint(Bnode p);
	void posPrint(Bnode p);
	void midPrint_useNoRecursion(Bnode p);
};
void  BinaryTree::creatTree(Broot& p) {
	char c;
	c = getchar();
	if (c == '#') {
		p = NULL;
	}
	else {
		p = new node;
		p->lchild = NULL;
		p->rchild = NULL;
		p->data = c;
		creatTree(p->lchild);
		creatTree(p->rchild);
	}
}
void BinaryTree::prePrint(Bnode p)
{
	if (p == NULL)return;
	cout << p->data;
	prePrint(p->lchild);
	prePrint(p->rchild);
}
void BinaryTree::midPrint(Bnode p)
{
	if (p == NULL)return;
	midPrint(p->lchild);
	cout << p->data;
	midPrint(p->rchild);
}
void BinaryTree::posPrint(Bnode p)
{
	if (p == NULL)return;
	posPrint(p->lchild);
	posPrint(p->rchild);
	cout << p->data;
}
void BinaryTree::midPrint_useNoRecursion(Bnode p)
{
	struct stack_ptr {
		Node* arr[20] = { NULL };
		int base = 0;
		int top = 0;
		void push(Node* p) {
			arr[top++] = p;
		}
		Node* pop() {
			cout << arr[top - 1]->data;
			top--;
			return arr[top];
		}
	};
	stack_ptr s;
	while (p != NULL || s.top != 0) {
		while (p != NULL) { s.push(p); p = p->lchild; };
		if (s.top != s.base) {
			p = s.pop();
			p = p->rchild;
		}
	}
}

int main()
{
	BinaryTree b;
	b.creatTree(b.root);

	cout << "先序遍历" << endl;
	b.prePrint(b.root);
	cout << endl;
	cout << "中序遍历" << endl;
	b.midPrint(b.root);
	cout << endl;
	cout << "非递归的中序遍历" << endl;
	b.midPrint_useNoRecursion(b.root);
	cout << endl;
	cout << "后序遍历" << endl;
	b.posPrint(b.root);
	cout << endl;

	return 0;
}

输入及运行结果:

 至此,用类实现的二叉树结束了。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值