目录
一、二叉树的遍历
(1)递归方式
递归方式要注意递归的三要素:
1.确定递归函数的参数和返回值
二叉树的遍历传入的参数为树的根节点,因为每次遍历都是通过根节点得到其左右子树,递归遍历其所有节点,返回值为void,因为不需要返回什么,如下传入所示:
void PreOrder(BitNode* root);//先序遍历
2.确定终止条件
递归需要找到某个终止条件,不然会一直递归下去,终止条件也就是我们常说的出口,二叉树的递归出口当然为节点为空的时候就返回,如下就为出口:
if (!root)//根节点为空,返回 { return; }
3.确定递归逻辑
比如先序排序的逻辑为,先访问根节点,接着访问左子树,最后访问右子树,每个节点都重复以上三步,知道节点为空,从出口出去,如下为三种遍历的逻辑关系:
- 先序遍历
cout << root->data << " ";//按照先序遍历规则,先输出根节点,在遍历左子树,最后遍历右子树 PreOrder(root->lchild);//左 PreOrder(root->rchild);//右
- 中序遍历
InOrder(root->lchild);//先遍历左子树 cout << root->data << " ";//当左子树为空时,返回输出根节点 InOrder(root->rchild);//接着遍历右子树
- 后序遍历
PostOrder(root->lchild);//遍历左子树 PostOrder(root->rchild);//遍历右子树 cout << root->data << " ";//输出根节点
以上确认后,递归代码迎刃而解了
1.先序遍历
void BitTree::PreOrder(BitNode* root)
{
if (!root)//根节点为空,返回
{
return;
}
else
{
cout << root->data << " ";//按照先序遍历规则,先输出根节点,在遍历左子树,最后遍历右子树
PreOrder(root->lchild);
PreOrder(root->rchild);
}
}
2.中序遍历
void BitTree::InOrder(BitNode* root)
{
if (!root)//根节点为空,返回
{
return;
}
else
{
InOrder(root->lchild);//先遍历左子树
cout << root->data << " ";//当左子树为空时,返回输出根节点
InOrder(root->rchild);//接着遍历右子树
}
}
3.后序遍历
void BitTree::PostOrder(BitNode* root)
{
if (!root)
{
return;
}
else
{
PostOrder(root->lchild);//遍历左子树
PostOrder(root->rchild);//遍历右子树
cout << root->data << " ";//输出根节点
}
}
递归方式还是比较简单的,在这里我强调一下,用递归写代码的时候,千万不要去深究遍历过程,不然你自己会绕晕过去。
(2)非递归方式(迭代法)
1.先序遍历
前序遍历是中左右,每次先访问根节点,然后输出根节点,接着遍历其左右子树。在这我们用到栈,因为栈的特点是先进后出,所以首先把根节点入栈,然后取出根节点后,输出,接着把其右孩子和左孩子入栈,注意这里先放入右孩子,这样从栈中取节点的时候,先取左孩子,下面看代码:
void BitTree::PreOrder()
{
stack<BitNode*> s;//定义栈,存放树节点
if (!root)
{
return;
}
s.push(root);//首先树根节点入栈
while (!s.empty())//当栈不为空的时候,说明还有节点没有输出
{
BitNode* node = s.top();//栈顶出栈
s.pop();//弹栈,避免重复访问
cout << node->data << " ";//输出根节点(每个节点都可看作根节点,因为每个节点都能看作一颗树的根节点)
if (node->rchild)//右节点不为空,把右子树压栈,因为栈的特点为先进后出,先序遍历访问左子树在前,所以先压栈右子树
{
s.push(node->rchild);
}
if (node->lchild)
{
s.push(node->lchild);
}
}
cout << endl;//换行
}
2.中序遍历
我们在比较递归代码的时候发现,整体代码都差不多,只是部分代码顺序不一样,迭代法写中序遍历的时候,不能在先序的基础上做简易变更,因为在处理先序遍历的时候,其遍历顺序为根左右,其遍历顺序和处理节点的顺序都是一样的,遍历到根节点就输出跟节点,但是中序遍历的顺序为左根右,其遍历顺序和处理节点的顺序不一样,中序遍历为一直遍历根节点的左孩子,知道根节点左孩子为空,所以我们在处理中序遍历的时候可以定义一个临时指针辅助用来遍历节点,栈则用来处理节点,下面来看下代码:
void BitTree::InOrder()
{
stack<BitNode*> s;
if (!root)
{
return;
}
BitNode* node = root;//定义临时指针保存根节点
while (!s.empty()||node)//节点不为空或者栈不为空时
{
while (node)//循环遍历左子树
{
s.push(node);
node = node->lchild;//左
}
node = s.top();//左子树为空时,结束循环,栈顶元素出栈
s.pop();//删除栈顶元素
cout << node->data << " ";//输出根
node = node->rchild;//右子树
}
cout << endl;
}
3.后序遍历
后序遍历其实是比较难得,这里我们稍加分析就会迎刃而解。首先观察前序遍历,其遍历顺序为根左右,而后序遍历其顺序为左右根,是不是发现有点类似,把前序遍历的结果倒着输出就是右左根的顺序,和后序遍历不同的就为左右的顺序,那么我们在之前先序遍历的基础下可以调换一下左右子树的入栈顺序,我们先把左子树入栈,后入右子树,这样我们得到的先序遍历顺序不就是根右左了吗,然倒着输出不就是左右根的顺序了吗,这样便得到了后序遍历结果。在这我定义了一个vector容器,用来保存输出的节点值,最后倒着输出这个容器里面的元素便为后序遍历结果,下面看代码:
void BitTree::PostOrder()
{
vector<int> v;//定义容器保存节点值
stack<BitNode*> s;
if (!root)
{
return;
}
s.push(root);
//类似于先序遍历,遍历完后vector容器里保存的数为根右左型,倒置v后便得到后序遍历
while (!s.empty())
{
BitNode* node = s.top();
v.push_back(node->data);//根
s.pop();
if (node->lchild)
{
s.push(node->lchild);//左
}
if (node->rchild)
{
s.push(node->rchild);//右
}
}
vector<int>::reverse_iterator iter = v.rbegin();//定义反向迭代器
//逆序输出容器元素,得到后序遍历结果
for (; iter != v.rend(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
4.层序遍历
以上三种遍历方式其实就是深度优先遍历,而层序遍历就是广度优先遍历,根据从上到下,从左到右的顺序遍历每个节点,也就是一层 一层的遍历。这里我们用到队列,首先把第一层的一个节点入队列,然后在取出,然后在把该节点的左右孩子入队列,也就是接下来我们将要遍历的第二层,根据队列先进先出的特点,每次先把左孩子入队列,重复以上步骤,直至遍历所有的节点,下面看代码:
void BitTree::LevelOrder()
{
queue<BitNode*> q;
if (!root)
{
return;
}
q.push(root);//根节点入队列
while (!q.empty())//队列不为空,队列为空q.empty()为true,否则为false,!q.empty()则为真
{
int size = q.size();
//循环遍历每一层的节点
for (int i = 0; i < size; i++)
{
BitNode* node = q.front();//取队列首部
q.pop();//每取一个出栈
cout << node->data << " ";
//左子树不为空,入队列,注意先判断左边,因为队列为先进先出
if (node->lchild)
{
q.push(node->lchild);//左
}
if (node->rchild)
{
q.push(node->rchild);//右
}
}
}
cout << endl;
}
二、求二叉树节点数
以上四种遍历方式弄明白后,求节点数以及叶子节点数就相对来说比较容易了,基本就是在遍历基础上稍作更改,把输出那段代码更改无节点数+1,这里我就用先序遍历的方式求节点数以及叶子节点数。
1.求二叉树结点数
这里用到静态变量,NodeSize,保证递归过程中只初始化一次。求节点数基本上就在先序遍历基础上稍加更改。这里我用递归法求,用迭代法也可以,也只是在迭代遍历的那段代码上稍加修改,这里我就不对赘述了。
int BitTree::BinaryTreeNodeSize(BitNode* root)
{
static int NodeSize = 0;//定义成静态变量,数据共享,只初始化一次
if (!root)
{
return 0;
}
//类似于先序遍历,每遍历一个节点节点数+1,继续递归遍历该节点的左右孩子
NodeSize++;
BinaryTreeNodeSize(root->lchild);
BinaryTreeNodeSize(root->rchild);
return NodeSize;
}
2.求二叉树叶子节点数
这里我们发现和求节点数差不多,只不过是变量自增的条件不同而已。这里我用到了递归,迭代同样也可以,方法差不多,这里就不多加赘述了。
int BitTree::LeafNodeSize(BitNode* root)
{
static int leafNode = 0;
if (!root)
{
return 0;
}
if (!root->lchild&&!root->rchild)
{
leafNode++;
}
LeafNodeSize(root->lchild);
LeafNodeSize(root->rchild);
return leafNode;
}
三、求二叉树的高度
这里求二叉树的高度用递归法和迭代法都可以,下面我简单讲下这两种方法。
1.递归求二叉树高度
int BitTree::BinaryTreeHeight(BitNode* root)
{
//根节点为空,为空树,高度为0
if (!root)
{
return 0;
}
int lheight = BinaryTreeHeight(root->lchild);//左子树深度
int rheight = BinaryTreeHeight(root->rchild);//右子树深度
return 1 + (lheight > rheight ? lheight : rheight);
}
2.迭代求二叉树高度
迭代法求二叉树高度要用到层序遍历,从第一层开始,每次开始遍历一层之前,树高度加1,然后遍历当前层的每个节点时,把对应的左右孩子添加到队列,也就是我们接下来要遍历的下一层,直至遍历完每一层,返回树的高度。
int BitTree::BinaryTreeHeight()
{
/*
非递归方式求二叉树的深度要用到层序遍历,
每遍历一层,树的深度加1
*/
queue<BitNode*> q;
int TreeHeight = 0;//树的高度
if (!root)
{
return TreeHeight;
}
q.push(root);//根节点入队列,相当于二叉树的第一层
while (!q.empty())//队列不为空
{
int size = q.size();//每层的节点数
TreeHeight++;//while循环每循环一次说明正在遍历某一层的节点,则树深度+1
for (int i = 0; i < size; i++)
{
BitNode* node = q.front();
q.pop();
if (node->lchild)
{
q.push(node->lchild);//左
}
if (node->rchild)
{
q.push(node->rchild);//右
}
}
}
return TreeHeight;
}
四、整个代码段
#include<iostream>
using namespace std;
#include<stack>
#include<vector>
#include<queue>//队列头文件,层序遍历要用
typedef struct BitNode
{
int data;//节点数据域
BitNode *lchild;//指向左孩子指针域
BitNode *rchild;//指向右孩子指针域
};
class BitTree
{
public:
BitTree()
{
root = nullptr;
}
//释放根节点
~BitTree()
{
delete root;
root = nullptr;
}
void CreateBinaryTree(BitNode*& root,int val);//创建二叉树
int BinaryTreeHeight(BitNode* root);//求二叉树深度
int BinaryTreeHeight();//求二叉树深度(非递归方式)
int BinaryTreeNodeSize(BitNode* root);//求二叉树节点数
int LeafNodeSize(BitNode* root);//求二叉树叶子节点数
int LeafNodeSize();//求二叉树叶子节点数(非递归方式)
void PreOrder(BitNode* root);//先序遍历
void PreOrder();//先序非递归实现
void InOrder(BitNode* root);//中序遍历
void InOrder();//中序非递归实现
void PostOrder(BitNode* root);//后序遍历
void PostOrder();//后续非递归实现
void LevelOrder();//层序遍历
private:
BitNode *root;//树的根结点
};
//创建二叉树
void BitTree::CreateBinaryTree(BitNode*& root,int val)
{
/*
以二叉排序树的规则构造二叉树,也就是比根节点小的放到其左子树,
反之放到右子树
*/
//树根节点为空
if (!root)
{
root = new BitNode;//为根节点分配内存
root->data = val;//数据域赋值
root->lchild = nullptr;//左子树为置空
root->rchild = nullptr;//右子树置空
}
else
{
if (val <= root->data)//插入到左子树
{
CreateBinaryTree(root->lchild,val);//递归构建左子树
}
else
{
CreateBinaryTree(root->rchild,val);//递归构建右子树
}
}
this->root = root;//树根节点赋值
}
//先序遍历(递归方式)
void BitTree::PreOrder(BitNode* root)
{
if (!root)//根节点为空,返回
{
return;
}
else
{
cout << root->data << " ";//按照先序遍历规则,先输出根节点,在遍历左子树,最后遍历右子树
PreOrder(root->lchild);
PreOrder(root->rchild);
}
}
//先序遍历(非递归方式)
void BitTree::PreOrder()
{
stack<BitNode*> s;//定义栈,存放树节点
if (!root)
{
return;
}
s.push(root);//首先树根节点入栈
while (!s.empty())//当栈不为空的时候,说明还有节点没有输出
{
BitNode* node = s.top();//栈顶出栈
s.pop();//弹栈,避免重复访问
cout << node->data << " ";//输出根节点(每个节点都可看作根节点,因为每个节点都能看作一颗树的根节点)
if (node->rchild)//右节点不为空,把右子树压栈,因为栈的特点为先进后出,先序遍历访问左子树在前,所以先压栈右子树
{
s.push(node->rchild);
}
if (node->lchild)
{
s.push(node->lchild);
}
}
cout << endl;//换行
}
//中序遍历(递归遍历)
void BitTree::InOrder(BitNode* root)
{
if (!root)//根节点为空,返回
{
return;
}
else
{
InOrder(root->lchild);//先遍历左子树
cout << root->data << " ";//当左子树为空时,返回输出根节点
InOrder(root->rchild);//接着遍历右子树
}
}
//中序遍历(非递归方式)
void BitTree::InOrder()
{
stack<BitNode*> s;
if (!root)
{
return;
}
BitNode* node = root;//定义临时指针保存根节点
while (!s.empty()||node)//节点不为空或者栈不为空时
{
while (node)//循环遍历左子树
{
s.push(node);
node = node->lchild;//左
}
node = s.top();//左子树为空时,结束循环,栈顶元素出栈
s.pop();//删除栈顶元素
cout << node->data << " ";//输出根
node = node->rchild;//右子树
}
cout << endl;
}
//后序遍历(递归遍历)
void BitTree::PostOrder(BitNode* root)
{
if (!root)
{
return;
}
else
{
PostOrder(root->lchild);//遍历左子树
PostOrder(root->rchild);//遍历右子树
cout << root->data << " ";//输出根节点
}
}
//后序遍历(非递归方式)
void BitTree::PostOrder()
{
vector<int> v;//定义容器保存节点值
stack<BitNode*> s;
if (!root)
{
return;
}
s.push(root);
//类似于先序遍历,遍历完后vector容器里保存的数为根右左型,倒置v后便得到后序遍历
while (!s.empty())
{
BitNode* node = s.top();
v.push_back(node->data);//根
s.pop();
if (node->lchild)
{
s.push(node->lchild);//左
}
if (node->rchild)
{
s.push(node->rchild);//右
}
}
vector<int>::reverse_iterator iter = v.rbegin();//定义反向迭代器
//逆序输出容器元素,得到后序遍历结果
for (; iter != v.rend(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
//求二叉树的深度(递归实现)
int BitTree::BinaryTreeHeight(BitNode* root)
{
//根节点为空,为空树,高度为0
if (!root)
{
return 0;
}
int lheight = BinaryTreeHeight(root->lchild);//左子树深度
int rheight = BinaryTreeHeight(root->rchild);//右子树深度
return 1 + (lheight > rheight ? lheight : rheight);
}
//求二叉树的深度(非递归方式)
int BitTree::BinaryTreeHeight()
{
/*
非递归方式求二叉树的深度要用到层序遍历,
每遍历一层,树的深度加1
*/
queue<BitNode*> q;
int TreeHeight = 0;//树的高度
if (!root)
{
return TreeHeight;
}
q.push(root);//根节点入队列,相当于二叉树的第一层
while (!q.empty())//队列不为空
{
int size = q.size();//每层的节点数
TreeHeight++;//while循环每循环一次说明正在遍历某一层的节点,则树深度+1
for (int i = 0; i < size; i++)
{
BitNode* node = q.front();
q.pop();
if (node->lchild)
{
q.push(node->lchild);//左
}
if (node->rchild)
{
q.push(node->rchild);//右
}
}
}
return TreeHeight;
}
//层序遍历
void BitTree::LevelOrder()
{
queue<BitNode*> q;
if (!root)
{
return;
}
q.push(root);//根节点入队列
while (!q.empty())//队列不为空,队列为空q.empty()为true,否则为false,!q.empty()则为真
{
int size = q.size();
//循环遍历每一层的节点
for (int i = 0; i < size; i++)
{
BitNode* node = q.front();//取队列首部
q.pop();//每取一个出栈
cout << node->data << " ";
//左子树不为空,入队列,注意先判断左边,因为队列为先进先出
if (node->lchild)
{
q.push(node->lchild);//左
}
if (node->rchild)
{
q.push(node->rchild);//右
}
}
}
cout << endl;
}
//求二叉树节点数
int BitTree::BinaryTreeNodeSize(BitNode* root)
{
static int NodeSize = 0;//定义成静态变量,数据共享,只初始化一次
if (!root)
{
return 0;
}
//类似于先序遍历,每遍历一个节点节点数+1,继续递归遍历该节点的左右孩子
NodeSize++;
BinaryTreeNodeSize(root->lchild);
BinaryTreeNodeSize(root->rchild);
return NodeSize;
}
//求二叉树叶子节点数
int BitTree::LeafNodeSize(BitNode* root)
{
static int leafNode = 0;
if (!root)
{
return 0;
}
if (!root->lchild&&!root->rchild)
{
leafNode++;
}
LeafNodeSize(root->lchild);
LeafNodeSize(root->rchild);
return leafNode;
}
//求二叉树叶子节点数(非递归方式)
int BitTree::LeafNodeSize()
{
int LeafSize = 0;
stack<BitNode*> s;
if (!root)
{
return 0;
}
s.push(root);
while (!s.empty())
{
BitNode* node = s.top();
s.pop();
if (!node->lchild&&!node->rchild)
{
LeafSize++;
}
if (node->lchild)
{
s.push(node->lchild);
}
if (node->rchild)
{
s.push(node->rchild);
}
}
return LeafSize;
}
int main()
{
BitTree tree;
BitNode *phead = nullptr;
int val;
while (cin >> val)
{
tree.CreateBinaryTree(phead,val);
}
//先序输出
cout << "先序输出:" << endl;
tree.PreOrder(phead);
cout << endl;
tree.PreOrder();
//中序输出
cout << "中序输出:" << endl;
tree.InOrder(phead);
cout << endl;
tree.InOrder();
//后序输出
cout << "后序输出:" << endl;
tree.PostOrder(phead);
cout << endl;
tree.PostOrder();
//层序遍历
cout << "层序输出:" << endl;
tree.LevelOrder();
//二叉树的深度
cout << "树的深度:" << endl;
cout << tree.BinaryTreeHeight(phead) << endl;
cout << tree.BinaryTreeHeight() << endl;
//二叉树的节点数
cout << "节点数:" << endl;
cout << tree.BinaryTreeNodeSize(phead) << endl;
//二叉树的叶子节点数
cout << "叶子节点:" << endl;
cout << tree.LeafNodeSize(phead) << endl;
cout << tree.LeafNodeSize() << endl;
return 0;
}