博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++实现二叉树结构及其二叉树常用算法
阅读量:723 次
发布时间:2019-03-21

本文共 8043 字,大约阅读时间需要 26 分钟。

文章目录

头文件

使用node 定义节点,并重命名为BTNode。二叉树类 BTree中含有一个成员BTNode* head;存放二叉树的头节点。

typedef int ElemType;const int  MaxSize = 20;//二叉树链式存储结构typedef struct node {
ElemType data; struct node* lchild; struct node* rchild;}BTNode;class BTree {
BTNode* head;public: BTree() {
head = new BTNode; }; BTree(const char* str); void CreateBTNode(const char* str);创建二叉树(对于括号表示法) void DispBTree();//输出二叉树 BTNode* FindNode(ElemType x);//查找节点 //查找左右孩子节点 BTNode* LchildNode(BTNode* b); BTNode* RchildNode(BTNode* b); //求树高 int BTreeHeight(); //递归遍历 void PreOrder();//先序遍历 void InOrder();//中序遍历 void PostOrder();//后序遍历 //非递归遍历 void PreOrder1();//先序遍历 void InOrder1();//中序遍历 void PostOrder1();//后序遍历 void LevelOrder();//层次遍历 //构造二叉树 BTNode* preInCreate(char* pre, char* in, int n);//前序和中序 BTNode* postInCreate(char* post, char* in, int n);//前序和后序public: BTNode* getHead() {
return head; } void setHead(BTNode* _head) {
head = _head; } void DispBTree(BTNode* b);//输出二叉树private: void AllPath1(BTNode* b);//P181 BTNode* FindNode(BTNode *b,ElemType x);//查找节点 int BTreeHeight(BTNode *b); void PreOrder(BTNode* b);//先序遍历 void InOrder(BTNode* b);//中序遍历 void PostOrder(BTNode* b);//后序遍历 };

成员函数

构造函数

两个构造函数:一个无参构造,只为头节点开辟空间。一个有参构造,传入一个以括号表示法表示的树的const char*字符串对象。

BTree::BTree(const char* str) {
CreateBTNode(str);}

创建二叉树CreateBTNode

使用void CreateBTNode(const char* str);方法创建。

创建过程使用栈作为辅助,对于一个合法的字符串,遍历其中所有元素,如果遇到一个节点元素(default部分),初始化节点,若该节点作为头节点,则执行this->head = p;,若不是头节点,通过k判断该节点作为栈顶元素的左孩子节点或者右孩子节点。如果遇到(,此时p已经被赋值,其值为上一个遍历到的节点元素,(表明该节点p将作为之后节点的父节点,将p进栈,并将k置为1。如果遇到,,表明某个节点的左子树处理完,将要处理右孩子。如果遇到),表明当前栈顶元素的孩子节点已经处理完,将其出栈。

void BTree::CreateBTNode(const char* str) {
BTNode* St[MaxSize], * p = NULL; int top = -1;//栈顶元素下标 int k = 1;//标记当前处理的是左子树还是右子树 int j = 0;//作为遍历str时的下表 char ch; //存储每次取出的一个字符 this->head = NULL; ch = str[j]; while (ch != '\0') {
switch (ch) {
case'(': //遇到左括号,表明该元素将作为父节点,将该元素进栈, ++top;//第一次top++时,将使得top从-1变为0 St[top] = p; k = 1;//标记以后处理的节点为该节点的左孩子 break; case')'://遇到右括号,表明某个元素的子节点处理完毕,将该元素出栈 top--; break; case','://遇到逗号,准备处理右节点 k = 2; break; default: //遇到一个元素时,节点初始化,然后如果当前树为空,将节点赋给根节点,然后判断该节点为栈顶元素的左子树还是右子树,将其放到对应位置。 p = new BTNode; p->data = ch; p->lchild = p->rchild = NULL; if (this->head == NULL) this->head = p; else {
switch (k) {
case 1: St[top]->lchild = p; break; case 2: St[top]->rchild = p; break; } } } //获取下一个元素 ch = str[++j]; }}

输出二叉树DispBTree

BTree类提供公共接口void DispBTree();用于输出二叉树,并void DispBTree(BTNode* b);中实现接口

输出过程通过递归实现,并以括号表示法格式输出。

void  BTree::DispBTree() {
this->DispBTree(this->head); cout << endl;}void BTree::DispBTree(BTNode* b) {
if (b == NULL) return; cout << (char)b->data; if (b->lchild != NULL || b->rchild != NULL) {
cout << "("; this->DispBTree(b->lchild); if (b->rchild != NULL) cout << ","; this->DispBTree(b->rchild); cout << ")"; } }

查找指定节点FindNode

BTNode* FindNode(ElemType x);方法按照元素data域进行查找,通过调用BTNode* FindNode(BTNode *b,ElemType x);以递归方式实现。

BTNode* BTree::FindNode(ElemType x) {
return this->FindNode(this->head, x);}BTNode* BTree::FindNode(BTNode* b, ElemType x) {
if (b == NULL) return NULL; if (b->data == x) return b; else {
BTNode* p = FindNode(b->lchild, x); if (p != NULL) return p; else return FindNode(b->rchild, x); } }

查找左、右孩子节点

BTNode* BTree::LchildNode(BTNode* b) {
return b->lchild;}BTNode* BTree::RchildNode(BTNode* b) {
return b->rchild;}

求树高BTreeHeight

通过调用私有成员函数int BTreeHeight(BTNode* b);以递归方式来实现。

int BTree::BTreeHeight() {
return BTreeHeight(this->head);}int BTree::BTreeHeight(BTNode* b) {
if (b == NULL) return 0; if (b->lchild == NULL && b->rchild == NULL) return 1; else {
int lheight = BTreeHeight(b->lchild); int rheight = BTreeHeight(b->rchild); return (lheight > rheight) ? (lheight + 1) : (rheight + 1); }}

递归遍历

三种递归遍历过程如下,都是通过调用私有同名函数实现。

void BTree::PreOrder() {
this->PreOrder(this->head); cout << endl;}void BTree::InOrder() {
this->InOrder(this->head); cout << endl;}void BTree::PostOrder() {
this->PostOrder(this->head); cout << endl;}void BTree::PreOrder(BTNode* b) {
if (b != NULL) {
cout << (char)b->data << " "; PreOrder(b->lchild); PreOrder(b->rchild); }}void BTree::InOrder(BTNode* b) {
if (b != NULL) {
InOrder(b->lchild); cout << (char)b->data << " "; InOrder(b->rchild); }}void BTree::PostOrder(BTNode* b) {
if (b != NULL) {
PostOrder(b->lchild); PostOrder(b->rchild); cout << (char)b->data << " "; }}

非递归遍历

对于非递归遍历,均采用栈作为辅助,以此获得在遍历其左右孩子节点后还能访问该节点的能力。

前序遍历

先将根节点进栈后,在while循环中,每次从栈中取出栈顶节点进行访问,然后如果有右孩子节点,将右孩子节点入栈,如果有左孩子节点,再将左孩子节点入栈。先入栈右孩子再入栈左孩子是为了使得出栈时左孩子先出栈。

void BTree::PreOrder1() {
BTNode* St[MaxSize], * b ,* p; int top = -1; b = this->head; if (b != NULL) {
St[++top] = b;//根节点进栈 while (top >= 0) {
p = St[top--]; //访问当前节点 cout << (char)p->data << " "; //右节点入栈, if (p->rchild != NULL) St[++top] = p->rchild; //左节点入栈,左节点在右节点后入栈,使得左节点在右节点前出栈 if (p->lchild != NULL) St[++top] = p->lchild; } } cout << endl;}

中序遍历

在嵌套的while循环中(条件为p != NULL的那个),每次将当前节点进栈并不断指向当前节点的左孩子节点,这使得每次退出while循环时,当前栈顶元素的左孩子节点为空(或已经被访问过)。而在下面的if(条件为top >= 0那个)语句块中,每次将当前栈顶元素出栈并访问后,让p指向它的右孩子节点,使得一个具有左右子树的节点将在其左子树后,右子树前被访问。

在while循环中,top >= 0 || p != NULL这个条件是为了

void BTree::InOrder1(){
BTNode* St[MaxSize], * b, * p; int top = -1; b = this->head; if (b != NULL) {
p = b; while (top >= 0 || p != NULL) {
while (p != NULL) {
//扫描p所有左节点便一次进栈 St[++top] = p; p = p->lchild; } if (top >= 0) {
p = St[top--]; cout << (char)p->data << " "; p = p->rchild;//转向处理p的右孩子节点 } } }}

后序遍历

最外层while循环使用do{}while (top != -1);是因为第一次进入是栈为空,第一次进入该while循环不对条件检查。在内层第一个while中,不断将当前节点进栈后,指向左孩子节点,使得退出该while循环后,栈顶元素的左孩子节点为空。在内层第二个while循环中,第一次进入时,由于p=NULL;如果当前节点没有右子树,将被输出,同时让p指向当前被访问的节点,这样在该while中进行b->rchild == p判断,将可以直知道该节点的右子树是否已被访问或为空。

void BTree::PostOrder1() {
BTNode* St[MaxSize], * b, * p; int flag; int top = -1; b = this->head; if (b != NULL) {
do {
//当栈不为空时循环,第一次进入循环不检查栈是否为空 while (b != NULL) {
St[++top] = b; b = b->lchild; } //到此,栈顶元素没有左孩子节点或左子树均已被访问 p = NULL;//p指向栈顶节点的前一个已经被访问过的节点 flag = 1;//标记左孩子已访问过或为空 while (top != -1 && flag) {
b = St[top]; if (b->rchild == p) {
cout << (char)b->data << " ";; top--; p = b;//p指向刚访问过的节点,以便于马上进行的下一次if判断 } else {
//存在右孩子 b = b->rchild; flag = 0; } } } while (top != -1); }}

层序遍历

void BTree::LevelOrder() {
BTNode* p ,*b = this->head; BTNode* queue[MaxSize]; int front, rear;//front指向队头的前一个元素,rear指向队尾元素 front = rear = -1; queue[++rear] = b; while (front != rear) {
//当front==rear时表示队列为空 front = (front + 1) % MaxSize; p = queue[front]; cout << (char)p->data << " "; if (p->lchild != NULL) {
rear = (rear + 1) % MaxSize; queue[rear] = p->lchild; } if (p->rchild != NULL) {
rear = (rear + 1) % MaxSize; queue[rear] = p->rchild; } }}

构造二叉树

在这里插入图片描述

假设元素为char类型且不重复。对于如上二叉树

其括号表示法表示为:A(B(D(,G)),C(E,F)).
前序遍历结果:A B D G C E F
中序遍历结果:D G B A E C F
后序遍历结果:G D B E F C A

前序中序

通过分析可知,使用前序(A B D G C E F)和中序(D G B A E C F)可以每次取出一个前序的第一个元素(A),然后在中序中找到相同的元素(中序中的A)作为分界点,中序中的该元素的左边部分(D G B )构成以该元素(A)为根的树的左子树,该元素(A)的右边部分( E C F)构成右子树。对于左右子树,可以采用同样的方式将其构造成树,即使用递归。代码如下:

BTNode* BTree::preInCreate(char* pre, char* in, int n){
if (n <= 0) return nullptr; BTNode* b = new BTNode(); b->data = *pre; char* p = in; int i;//划分左右子树 for (i = 0; i < n; ++i) {
if ((*p) == (*b).data) break; else ++p; } b->lchild = preInCreate(pre + 1, in, i); b->rchild = preInCreate(pre + i + 1, p + 1, n - i - 1); return b;}

后序中序

通过分析可知,使用后序(G D B E F C A)和中序(D G B A E C F)过程与前序中序构造二叉树类似,只是改成将后序遍历序列的最后一个元素(A)来作为左右子树的分界点。

BTNode* BTree::postInCreate(char* post, char* in, int n){
if(n<=0) return nullptr; BTNode* b = new BTNode(); b->data = *(post + n - 1);//将最后一个元素作为当前根节点 char* p=in;//用于遍历中序 int i; for (i = 0; i < n; ++i) {
if ((*p) == (*b).data) break; else ++p; } b->lchild = postInCreate(post, in, i); b->rchild = postInCreate(post + i, p+1, n - i - 1); return b; }

转载地址:http://lttgz.baihongyu.com/

你可能感兴趣的文章