先通过前序遍历的方法创建一棵二叉树
1、先序遍历、中序遍历、后序遍历(递归和不用递归)、双栈法遍历、标记法遍历
2、求树高、树宽
3、判断二叉树是否是完全二叉树。
4、广度、深度优先遍历二叉树
#include<iostream>
#include<stack>
#include<queue>
#define OK 1
using namespace std;
//定义一棵树的结构体包括根结点、左孩子,右孩子
typedef struct Tnode {
char data;
struct Tnode* lchild, * rchild;
}* Btree;
//前序遍历创建树
void build(Btree& T) {
char ch;
cin >> ch;
if (ch == '0') T = NULL;
else {
T = (Tnode*)malloc(sizeof(Tnode));
T->data = ch;
build(T->lchild);
build(T->rchild);
}
}
//前序递归 遍历
void PreOrder(Btree T) {
if (T != NULL) {
cout << T->data <<" ";
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
//中序递归 遍历
void InOrder(Btree T) {
if (T != NULL) {
InOrder(T->lchild);
cout << T->data << " ";
InOrder(T->rchild);
}
}
//后序递归 遍历
void PostOrder(Btree T) {
if (T != NULL) {
PostOrder(T->lchild);
PostOrder(T->rchild);
cout << T->data << " ";
}
}
//前序遍历非递归版本
//由于整个左子树被访问完才能访问右子树,故需要借助栈来保存当前子树的根节点
void PreOrder2(Btree T) {
stack<Tnode*> S;//开辟一个栈
Btree p = T;//迭代指针 开始指向root
while (p || !S.empty()) { //只要还有结点
if (p) { //如果当前结点不空
cout << p->data << " "; //则打印当前结点 因为是先序,遇到新结点就打印
S.push(p);//当前结点入栈
p = p->lchild;//访问其左子树,向左访问结点
}
else {
p = S.top();//如果当前结点是空的,就退回到上一个结点
S.pop();//退栈
p = p->rchild;//访问其右子树
}
}
}
//中序非递归遍历 左 中 右
//根节点必须在左结点访问之后再访问,而根节点又是先于左子节点被遍历到,故要设置一个栈来保存
void InOrder2(Btree T) {
stack<Tnode*> S;//开辟一个栈
Btree p = T;//迭代器
while (p || !S.empty()) { //如果还有结点
if (p) {//如果当前结点不为空
S.push(p);// 不要急于访问根节点,先压栈。
p = p->lchild;//访问左子树
}
else {//如果当前节点是空的。则需要退回到上一个结点
p = S.top();
S.pop();//退到该子树的根节点立即访问
cout << p->data << " ";
p = p->rchild;//继续访问右子树
}
}
}
//后序遍历非递归 双栈法
//由于是左 右 中,因此入栈次序是中右左,
//先看根节点,再看右孩子,再看左孩子
void PostOrder2(Btree T) {
stack<Tnode*> S; //遍历栈
stack<Tnode*> result; //结果栈
Btree p = T;//迭代器
while (p || !S.empty()) {
if (p) { //如果当前结点不为空
S.push(p);//结点入栈
result.push(p);//结点入结果栈
p = p->rchild;//看其右孩子结点
}
else { //如果当前结点为空 ,退到上一个结点
p = S.top();
S.pop();//退栈
p = p->lchild;//找其左孩子结点
}
}
//输出后序序列
while (!result.empty()) {
p = result.top();
result.pop();
cout << p->data << " ";
}
}
// 后序遍历 访问标记法
void postOrder3(Btree T) {
stack<Tnode*> S; //开辟一个栈
Tnode* p = T; //迭代器
Tnode* r = NULL; //保存最近访问过的结点
while (p || !S.empty()) {
if (p) { //如果当前结点不为空 则走到该子树的最左边
S.push(p);
p = p->lchild;
}
else { //若为空,退到上一个结点
p = S.top();
if (p->rchild && p->rchild != r) { //如果存在右子树并且右子树没有被访问过
p = p->rchild;//那么就转向右子树继续
S.push(p); // 保存子树根节点
p = p->lchild; //继续向左访问左子树
}
else {
p = S.top();
S.pop();//没有右子树那就可以访问根节点了
cout << p->data << " ";
r = p; //标记r为最近访问的结点
p = NULL; //当前结点置为空 ,因为这棵子树的根节点都访问完了说明这颗子树就全部访问完了,就等待退回上一个子树根节点
}
}
}
}
//层序遍历 队列
void levelOrder(Btree T) {
queue<Tnode*> Q;
Tnode* p = T;
Q.push(T);
while (!Q.empty()) {
p = Q.front();
Q.pop();
cout << p->data << " ";
if (p->lchild) Q.push(p->lchild);
if (p->rchild) Q.push(p->rchild);
}
}
//递归求树高
int treeDepth(Btree T) {
if (T == NULL) return 0;
int rightTree = treeDepth(T->rchild);
int leftTree = treeDepth(T->lchild);
return max(rightTree, leftTree) + 1;
}
//层序(广度遍历)遍历求树高
int levelDepth(Btree T) {
int level = 0;
if (!T) return 0;
queue<Tnode*> Q;
Tnode* last = T; //记录每层最后一个结点,第一层最后一个节点就是根节点
Tnode* front = nullptr, * rear = nullptr;//记录队首结点 队尾结点
Q.push(T);//根节点入队
while (!Q.empty()) {
front = Q.front();//取队首
Q.pop();//队首弹出
if (front->lchild) {
Q.push(front->lchild);//如果有左孩子那么左孩子入队
rear = front->lchild;
}
if (front->rchild) {
Q.push(front->rchild);//如果有右孩子那么右孩子入队
rear = front->rchild;
}
if (front == last) {//如果当前结点是该层最后一个结点
//cout<<front->data<<" ?? "<<rear->data<<endl;
level++;//那么层数+1 ,到达下一层
last = rear;//更新last,下一层的最后一个结点必然是最后入队的那个结点
}
}
return level;
}
//层次遍历(广度遍历)求树宽度
//在层次遍历基础上。增加对每一层结点数的统计,维护max
int levelWidth(Btree T) {
int level = 0;
if (!T) return 0;
queue<Tnode*> Q;
Tnode* last = T; //记录每层最后一个结点,第一层最后一个节点就是根节点
Tnode* front = nullptr, * rear = nullptr;//记录队首结点 队尾结点
int ans = 0;//记录最大宽度
int cur = 0;//记录当前层的宽度
Q.push(T);//根节点入队
while (!Q.empty()) {
front = Q.front();//取队首
Q.pop();//队首弹出
cur++;
if (front->lchild) {
Q.push(front->lchild);//如果有左孩子那么左孩子入队
rear = front->lchild;
}
if (front->rchild) {
Q.push(front->rchild);//如果有右孩子那么右孩子入队
rear = front->rchild;
}
if (front == last) {//如果当前结点是该层最后一个结点
//cout<<front->data<<" ?? "<<rear->data<<endl;
level++;//那么层数+1 ,到达下一层
last = rear;//更新last,下一层的最后一个结点必然是最后入队的那个结点
//到达每层最后一个结点时就知道了这一层的总的结点数
//维护最大值即可
ans = max(ans, cur);
cur = 0;
}
}
return ans;
}
//递归遍历求树的宽度
//相当于先序遍历一遍然后把每个结点所在的层数记录下来
//每访问一个新结点就把该层的count加上1,最终得到所有层的宽度
void treeWidth(Btree T, int level, int count[]) {
if (T == NULL) return; //如果子树树根为空就不要再统计下去
count[level]++;//树在第level层的宽度+1
treeWidth(T->lchild, level + 1, count);
treeWidth(T->rchild, level + 1, count);
}
int treeWidth(Btree T) {
int count[200] = { 0 };
int maxx = 0;
treeWidth(T, 0, count);
for (int i = 0; i < 200; i++) {
maxx = max(maxx, count[i]);
}
return maxx;
}
//判断时否是完全二叉树
//利用性质 完全二叉树与满二叉树在层次遍历上的联系和区别,前者是后者序列的一部分
bool isComplete(Btree T) {
queue<Tnode*> Q;//Q中存储层次遍历序列
Tnode* p;
if (!T) return true;//空树也当成完全二叉树 它是满二叉树
Q.push(T);//首结点入队
while (!Q.empty()) { //如果Q不空
p = Q.front(); // 取出队首
Q.pop();
if (p) { //如果当前结点不空,将其左右孩子入队,不管孩子空不空
Q.push(p->lchild);
Q.push(p->rchild);
}
else { //如果当前结点为空,那么之后的序列不能有不空的结点,因为层次遍历ABCD.E显然是非法的
while (!Q.empty()) {
p = Q.front();
Q.pop();
if (p) return false;
}
}
}
return true;
}
//另一种方法创建二叉树并进行深度、广度优先搜索
/*******************************************************************************************************/
int CreateTree(Btree& T, char chars[], int& i) {
cin >> chars[i];
if (chars[i] == '0') {
T = NULL;
}
else {
T = new Tnode;
T->data = chars[i];
CreateTree(T->lchild, chars, ++i); //递归构建左子树
CreateTree(T->rchild, chars, ++i); //递归构建右子树
}
return OK;
}
//深度优先遍历
int depthFirstSearch(Btree root) {
stack<Tnode*> nodeStack; //使用C++的STL标准模板库
nodeStack.push(root);
Tnode* node;
while (!nodeStack.empty()) {
node = nodeStack.top();
cout << node->data;//遍历根结点
nodeStack.pop();
if (node->rchild) {
nodeStack.push(node->rchild); //先将右子树压栈
}
if (node->lchild) {
nodeStack.push(node->lchild); //再将左子树压栈
}
}
return OK;
}
//广度优先遍历
int breadthFirstSearch(Btree root) {
queue<Tnode*> nodeQueue; //使用C++的STL标准模板库
nodeQueue.push(root);
Tnode* node;
while (!nodeQueue.empty()) {
node = nodeQueue.front();
nodeQueue.pop();
cout << node->data;//遍历根结点
if (node->lchild) {
nodeQueue.push(node->lchild); //先将左子树入队
}
if (node->rchild) {
nodeQueue.push(node->rchild); //再将右子树入队
}
}
return OK;
}
/***************************************************************************************************/
int main() {
Btree T;
build(T);
printf("构造完毕\n");
cout << "先序遍历" << endl;
PreOrder(T); cout << endl;
cout << "中序遍历" << endl;
InOrder(T); cout << endl;
cout << "后序遍历" << endl;
PostOrder(T); cout << endl;
cout << "非递归中序遍历" << endl;
InOrder2(T); cout << endl;
cout << "非递归先序遍历" << endl;
PreOrder2(T); cout << endl;
cout << "非递归后序遍历双栈法" << endl;
PostOrder2(T); cout << endl;
cout << "非递归后序遍历标记法" << endl;
postOrder3(T);
cout << "层序遍历" << endl;
levelOrder(T);
cout << "递归求树高:" << treeDepth(T) << endl;
cout << "层序求树高:" << levelDepth(T) << endl;
cout << "递归求树宽度:" << treeWidth(T) << endl;
cout << "非递归求树宽度: " << levelWidth(T) << endl;
cout << "是否是完全二叉树:" << isComplete(T) << endl;
//再输入一棵树完成来进行广度、深度遍历
/* ****************************************************************** */
cout << "请再输入一棵树进行过树的广度深度遍历" << endl;
char ch[30];
int index = 0;
CreateTree(T, ch, index);
cout << "深度优先遍历二叉树结果: " << endl;
depthFirstSearch(T);
cout << endl;
cout << "广度优先遍历的二叉树结果:" << endl;
breadthFirstSearch(T);
cout << endl;
return 0;
}
//ABC..DE.G..F..H..
//ABS..C..DE..FG.H..I..
//ABD..E..CF... 完全二叉树 ABD..E..CF..G..满二叉树