C++ 二叉树简单实现及推广

再介绍二叉树前先了解一下生活中常见的场景:猜价格(你猜一个数由对方告知你猜大了还是猜小了,知道猜对为止),我们以100以内为例,总共只需要七次就能猜对价格。如图:
在这里插入图片描述
在这里插入图片描述
好了,下面介绍二叉树的知识。

二叉树的定义:

递归形式给出的:一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。二又树的特点是每个结点最多有两个子女,分别称为该结点的左子女和右子女。在二又树中不存在度大于2的结点,并且二又树的子树有左、右之分,其子树的次序不能颠倒。二又树是分支数最大不超过2的有根有序树。它可能有5种不同的形态。
注意下面这种不能称为二叉树,只能称为,后面会有二叉树的性质。
在这里插入图片描述

1、空的二叉树:  就是结构体指针  tree = NULL
2、只有根节点的二叉树;
3、只有左子树的二叉树;
4、只有右子树的二叉树;
5、左右子树都存在:
    5.1 完全二叉树
    5.2 满二叉树

现在我们知道二叉树总共五种形态了,那么问题来了,思考一下:

三个结点的树有几种形态呢?
三个结点的二叉树又有几种形态呢?

第一种只有两种形态(下图的树1和树2)。
第二种有五种形态。
在这里插入图片描述

二叉树的基本概念

先来了解二叉树会用到的一些基本概念:
叶子节点:没有任何子节点的节点称为树叶,或叶子节点。
深度:对于任意节点N,其深度指的是从根节点到N的唯一路径的长。根的深度为0。深度最深的叶子节点的深度为树的深度。可以理解为:树根是一个入口,离树根越远,就越深。如上图:A、B、C的深度为1,D、E的深度为2。
: 对于任意节点N,从N到一片树叶的最远路径的长为N的高度。(只可以从从上到下不能经过从下到上的节点。)树叶的高为0。树的高为根的高。如上图,根的高度为2。A的高度为1,其他节点高度为0。
:结点拥有的子树数(注意和图论中的定义不同),树的度是树内各结点的度的最大值。
在这里插入图片描述

二叉树的分类:

上面二叉树的第五种形态又分为完全二叉树满二叉树
满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上。如图
在这里插入图片描述

满二叉树的特点:
1、叶子只能出现在最下一层
2、非叶子结点的度一定为2
3、在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。

完全二叉树:对一棵具有n和结点的二叉树按层序编号,编号为i的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同(层序遍历下文会讲),如图。
在这里插入图片描述

完全二叉树的特点:
1、叶子结点只能出现在最下两层;
2、最下层的叶子一定集中在左部连续位置;
3、倒数二层,若有叶子结点,一定都在右部连续位置;
4、如果结点度为1,则该结点只有左子树,而不存在右子树;
5、同样结点数的二叉树,完全二叉树的深度最小。

二叉树的性质:

1、在二叉树的第i层最多有2^(k-1)个结点;
2、深度为k的二叉树最少有k个结点,最多有(2^k) - 1 结点;
3、具有n个结点的完全二叉树的最小深度为log2(n+1);
4、对任何一棵非空二叉树,如果其叶结点数为n0,度为2的非叶节点数为n2,则n0 = n2+1;

二叉树的遍历(只介绍递归遍历,非递归本文最后直接给出代码,稍微复杂的有注释)

非递归遍历请另查资料。
二叉树的遍历常见的分为四个:
前序遍历,中序遍历,后序遍历,层序遍历
因为用的是递归的方法,在使用递归的时候最先判断的应该就是递归出口了,因此遍历的规则就是若二叉树为空,则返回。
前序遍历:先访问根结点,然后前序遍历左子树,再前序遍历右子树。
中序遍历:先中序遍历根结点的左子树,再访问根结点,最后中序遍历右子树。
后序遍历:从左到右先叶子后结点的方式遍历访问左右子树,最后访问根结点。
层序遍历:从树的第一层开始访问,从上而下逐层遍历。
注意上面的黑体部分。是前序遍历而不是遍历。好好理解下哦;

下面代码是四种遍历的实现:
建立的二叉树如下图:
在这里插入图片描述

#include<iostream>
#include<queue>
using namespace std;
struct TreeNode {
	int data;
	TreeNode* left;
	TreeNode* right;
	TreeNode(int x):
		data(x),left(NULL),right(NULL){}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
};
void preprintftree(TreeNode* root) {  // 前序遍历
	if (root) {
		cout << root->data;    // 打印根结点
		preprintftree(root->left); // 前序遍历左子树
		preprintftree(root->right); // 前序遍历右子树
	}
	else
		root = NULL;
}
void midprintftree(TreeNode* root) {  // 中序遍历
	if (root) {
		midprintftree(root->left);
		cout << root->data;
		midprintftree(root->right);
	}
	else
		root = NULL;
}
void postprintftree(TreeNode* root) {  // 后序遍历
	if (root) {
		postprintftree(root->left);
		postprintftree(root->right);
		cout << root->data;
	}
	else
		root = NULL;
}
void levelprintftree(TreeNode* T) {  // 层序遍历(非递归)
	queue<TreeNode*> queue;  //定义一个队列。数据类型是二叉树指针,不要是int,不然无法遍历
	queue.push(T);         //算法:根结点入队列
	while (!queue.empty()) { //若队列非空则循环执行下列的3个步骤
		T = queue.front();  //步骤1:对头元素出队,指针从新指向,front()方法是将返回队头元素	
		cout << T->data;   //队头元素出队然后将队头元素的左右孩子入队
		queue.pop();//pop是出队
		if (T->left != NULL) {//步骤2:左子树不空,将左子树入队
			queue.push(T->left);//入队的就是一个地址元素
		}
		if (T->right != NULL) {//步骤3:右子树不空,将右子树入队
			queue.push(T->right);
		}
	}
}
int main()
{
	TreeNode* T1 = new TreeNode(8);
	TreeNode* T2 = new TreeNode(8);
	TreeNode* T3 = new TreeNode(7);
	TreeNode* T4 = new TreeNode(9);
	TreeNode* T5 = new TreeNode(2);
	TreeNode* T6 = new TreeNode(4);
	TreeNode* T7 = new TreeNode(7);
	T1->left = T2; T1->right = T3; T2->left = T4; T2->right = T5; T3->left = T6; T3->right = T7;
	preprintftree(T1);
	cout << endl;
	midprintftree(T1);
	cout << endl;
	postprintftree(T1);
	cout << endl;
	levelprintftree(T1);
	return 0;
}

运行结果为:
在这里插入图片描述

注意:

	已知前序遍历和中序遍历可以确定唯一的二叉树;
	已知后序遍历和中序遍历可以确定唯一的二叉树;
	但是已知前序和后序不能确定。

三种遍历方法的六种实现以及层序遍历如下:

#include<iostream>
#include<stack>
#include<queue>
using namespace std;
struct TreeNode {
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode(int x):
		val(x),left(NULL),right(NULL){}
};
void preprintfTree(TreeNode* root) {  // 递归前序遍历
	if (root == nullptr) return;
	TreeNode* p = root;
	cout << p->val << ' ';
	preprintfTree(p->left);
	preprintfTree(p->right);
}
void midprintfTree(TreeNode* root) {  // 递归中序遍历
	if (root == nullptr) return;
	TreeNode* p = root;
	midprintfTree(p->left);
	cout << p->val << ' ';
	midprintfTree(p->right);
}
void postprintfTree(TreeNode* root) {  //递归后序遍历
	if (root == nullptr) return;
	TreeNode* p = root;
	postprintfTree(p->left);
	postprintfTree(p->right);
	cout << p->val << ' ';
}
void NrpreprintfTree(TreeNode* root) {  // 非递归前序遍历
	if (root == nullptr) return;
	TreeNode* p = root;
	stack<TreeNode*> _stack;
	while (p!= nullptr || !_stack.empty()) {
		while (p) {
			cout << p->val << ' ';
			_stack.push(p);
			p = p->left;
		}
		if (!_stack.empty()) {
			p = _stack.top();
			_stack.pop();
			p = p->right;
		}
	}
}
void NrmidprintfTree(TreeNode* root) {   // 非递归中序遍历
	if (root == nullptr) return;
	stack<TreeNode*> _stack;
	TreeNode* p = root;
	while (p != nullptr || !_stack.empty()) {
		while (p) {
			_stack.push(p);
			p = p->left;
		}
		if (!_stack.empty()) {
			p = _stack.top();
			cout << p->val << ' ';
			_stack.pop();
			p = p->right;
		}
	}
}
/*
由两个栈实现,
先把根节点压入第一个栈,
当栈1不为空时,
出栈,并压入第二个栈,然后将刚才出栈的栈顶元素的左右孩子入栈1;
当栈1空了,打印出栈2内的元素
栈1的栈顶优先为靠上层的右孩子再到下层的左孩子
栈2用于保存栈1的栈顶
入栈后符合后序遍历 上层右孩子后输出,下层左孩子先输出的顺序
*/
void NrpostprintfTree(TreeNode* root) {   // 非递归后序遍历,依赖双栈实现,相对来说容易理解
	if (root == nullptr) return;
	stack<TreeNode*> _stack1,_stack2;
	TreeNode* p = root;
	_stack1.push(p);
	while (!_stack1.empty()) {
		p = _stack1.top();
		_stack2.push(p);
		_stack1.pop();
		if (p->left)
			_stack1.push(p->left);
		if (p->right)
			_stack1.push(p->right);
	}
	while (!_stack2.empty()) {
		p = _stack2.top();
		cout << p->val << ' ';
		_stack2.pop();
	}
}
void levelprintfTree(TreeNode* root) {    // 非递归层序遍历
	if (root == nullptr) return;
	queue<TreeNode*> _queue;
	TreeNode* p = root;
	_queue.push(p);
	while ( !_queue.empty()) {
		p = _queue.front();
		cout << p->val << ' ';
		_queue.pop();
		if (p->left)
			_queue.push(p->left);
		if (p->right)
			_queue.push(p->right);
	}
}
int main()
{
	TreeNode* T1 = new TreeNode(8);
	TreeNode* T2 = new TreeNode(8);
	TreeNode* T3 = new TreeNode(7);
	TreeNode* T4 = new TreeNode(9);
	TreeNode* T5 = new TreeNode(2);
	TreeNode* T6 = new TreeNode(4);
	TreeNode* T7 = new TreeNode(7);
	T1->left = T2; T1->right = T3; T2->left = T4; T2->right = T5; T3->left = T6; T3->right = T7;
	cout << "递归前序遍历:";
	preprintfTree(T1);
	cout << endl;
	cout << "非递归前序遍历:";
	NrpreprintfTree(T1);
	cout << endl;
	cout << "递归中序遍历:";
	midprintfTree(T1);
	cout << endl;
	cout << "非递归中序遍历:";
	NrmidprintfTree(T1);
	cout << endl;
	cout << "递归后序遍历:";
	postprintfTree(T1);
	cout << endl;
	cout << "非递归后序遍历:";
	NrpostprintfTree(T1);
	cout << endl;
	cout << "非递归层序遍历:";
	levelprintfTree(T1);
	cout << endl;
	return 0;
}
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值