1.二叉树的基本操作
这里我有一个疑问:
在使用构造函数的时候,传参数的问题?
开始我是这么理解的------只使用指针(其实指针本身就是一个地址,相当于引用,也会改变root建立起二叉树),而2指针的引用,相当于就是对记录了指针的地址,采用了二次引用,其实是没有必要的,一次就够了。但是实际上用的时候并不是这样?根本不能建立二叉树,原因是因为开始指针指向的是一个不确定的位置?然后我又实验了,分配空间先,但是还是不行?所以不知道为什么一定要使用双重指针或指针引用?不知哪位高人能解答之,谢谢了
就是这样void creatTree(Node<T>* root);
//声明类BiTree及定义结构BiNode,文件名为bitree.h
#ifndef BITREE_H
#define BITREE_H
template <class T>
struct Node //二叉树的结点结构
{
T data;
Node<T> *lchild, *rchild;
};
//定义队列或栈空间大小
const int SIZE = 100;
template <class T>
class BiTree
{
public:
BiTree( ); //构造函数,初始化一棵二叉树,其前序序列由键盘输入(输入序列是前序遍历顺序)
BiTree(T* a, int n); //构造函数,输入序列是层序序列(非递归建立)
~BiTree(); //析构函数,释放二叉链表中各结点的存储空间
Node<T>* getRoot(); //获得指向根结点的指针
void preOrder(Node<T> *root); //前序遍历二叉树
void inOrder(Node<T> *root); //中序遍历二叉树
void postOrder(Node<T> *root); //后序遍历二叉树
void leverOrder(Node<T> *root); //层序遍历二叉树
void nrPreOrder(Node<T> *root); //前序遍历二叉树(非递归)
void nrInOrder(Node<T> *root); //中序遍历二叉树(非递归)
void nrPostOrder(Node<T> *root); //后序遍历二叉树(非递归)
//基本操作
Node<T>* searchNode(T x); //寻找值为x的节点
int biTreeDepth(Node<T>* root); //二叉树深度
Node<T>* getParent(Node<T>* root, T x); //如果存在值为data的节点,返回其父节点
Node<T>* getLeftSibling(Node<T>* root, T x); //获取左兄弟
Node<T>* getRightSibling(Node<T>* root, T x); //获取右兄弟
int leafCount(Node<T>* root); //叶子个数
int nodeCount(Node<T>* root); //节点个数
void deleteSibTree(Node<T>* root, T x); //删除节点x为根节点的子树
private:
Node<T> *root; //指向根结点的头指针
//1.使用双指针(指向指针的地址,这样就不在是拷贝,相当于一个引用)
//void creatTree(Node<T> **root); //有参构造函数调用(根地址的地址,也可指针的引用)
//2.使用引用(别名,可以直接对root操作)
void creatTree(Node<T>* &root);
//3.使用返回
//Node<T>* creatTree();
//4.只使用指针(其实指针本身就是一个地址,相当于引用,也会改变root建立起二叉树)
//而2指针的引用,相当于就是对记录了指针的地址,采用了二次引用,其实是没有必要的,一次就够了
//但是实际上用的时候并不是这样?根本不能建立二叉树,原因是因为开始指针指向的是一个不确定的位置?
//然后我又实验了,分配空间先,但是还是不行?所以不知道为什么一定要使用双重指针或指针引用?不知哪位高人能解答之,谢谢了
//void creatTree(Node<T>* root);
void createTree(Node<T>* &root, T* a, int n);//非递归建立二叉树
void search(Node<T>* root, T x, Node<T>* &p); //搜索
void releaseTree(Node<T> *root); //析构函数调用
};
#endif
2.基本操作实现
#include <iostream>
#include "bitree.h"
using namespace std;
//构造函数,初始化一棵二叉树,其前序序列由键盘输入
template <class T>
BiTree<T>::BiTree( ){
//creatTree(&root); //1.双指针(指向指针的地址,故而使用取地址操作符,取出指针root的地址)
creatTree(root); //2.root的引用
//root = creatTree(); //3.返回(实际上是this->root = createTree())
}
template <class T>
BiTree<T>::BiTree(T* a, int n){
createTree(root, a, n);
}
//析构函数,释放二叉链表中各结点的存储空间
template <class T>
BiTree<T>::~BiTree(){
releaseTree(root);
}
//获得指向根结点的指针
template <class T>
Node<T>* BiTree<T>::getRoot(){
return root;
}
//前序遍历二叉树
template <class T>
void BiTree<T>::preOrder(Node<T> *root){
if(root){
cout<<root->data<<" ";
preOrder(root->lchild);
preOrder(root->rchild);
}
}
//中序遍历二叉树
template <class T>
void BiTree<T>::inOrder(Node<T> *root){
if(root){
inOrder(root->lchild);
cout<<root->data<<" ";
inOrder(root->rchild);
}
}
//后序遍历二叉树
template <class T>
void BiTree<T>::postOrder(Node<T> *root){
if(root){
postOrder(root->lchild);
postOrder(root->rchild);
cout<<root->data<<" ";
}
}
//层序遍历二叉树
template <class T>
void BiTree<T>::leverOrder(Node<T> *root){
int front = 0;
int rear = 0; //采用顺序队列,并假定不会发生上溢
Node<T>* queue[SIZE];//存放结点指针的数组
Node<T>* t; //临时结点指针
if(root == NULL) return;
else{
queue[rear++] = root;
while(front != rear){
t = queue[front++];
if(t) cout<<t->data<<" ";
if(t->lchild) queue[rear++] = t->lchild;
if(t->rchild) queue[rear++] = t->rchild;
}
}
}
//层序遍历二叉树(利用循环队列)
/*
template <class T>
void BiTree<T>::leverOrder(Node<T> *root){
int front = 0;
int rear = 0; //采用顺序队列,并假定不会发生上溢
Node<T>* queue[SIZE];//存放结点指针的数组
Node<T>* t; //临时结点指针
if(root == NULL) return;
else{
queue[rear] = root;
rear = (rear+1)%SIZE;
while(front != rear){
t = queue[front];
front = (front+1)%SIZE;
if(t) cout<<t->data<<" ";
if(t->lchild){ queue[rear] = t->lchild; rear = (rear+1)%SIZE;}
if(t->rchild){ queue[rear] = t->rchild; rear = (rear+1)%SIZE;}
}
}
}*/
//-------------------------建立树的三种参数设置方法---------------------------------
//有参构造函数调用(前序递归建立树,输入顺序也必须是前序)
/*
template <class T>
void BiTree<T>::creatTree(Node<T> **root){
T ch;
cout<<"请输入创建一棵二叉树的结点数据"<<endl;
cin>>ch;
if(ch == "#") *root = NULL;
else{
*root = (Node<T>*)new Node<T>; //*root = new Node<T>;不过前面写法更好
(*root)->data = ch;
creatTree(&(*root)->lchild);
creatTree(&(*root)->rchild);
}
}
*/
/*
template <class T>
Node<T>* BiTree<T>::creatTree(){
Node<T>* root;
T ch;
cout<<"请输入创建一棵二叉树的结点数据"<<endl;
cin>>ch;
if(ch == "#") root = NULL;
else{
root = new Node<T>;
root->data = ch;
root->lchild = creatTree();
root->rchild = creatTree();
}
return root;
}
*/
template <class T>
void BiTree<T>::creatTree(Node<T>* &root){
T ch;
cout<<"请输入创建一棵二叉树的结点数据"<<endl;
cin>>ch;
if(ch == "#") root = NULL;
else{
root = new Node<T>;
root->data = ch;
creatTree(root->lchild);
creatTree(root->rchild);
}
}
template <class T>
void BiTree<T>::createTree(Node<T>* &root, T* s, int n){
Node<T>* queue[SIZE];
Node<T> *p, *m;
int rear = 0, front = 0;
if(s[0] == "#"){
root = NULL;
}else{
root = new Node<T>;
root->data = s[0];
queue[rear++] = root;
}
int i=1;//注意不管出不出栈,或者是空节点,i都要往前,出一次栈对应i前进2
while(front != rear && i<n)
{
p = queue[front++];
if(s[i] != "#"){
//出栈后遍历后面连续两个左右节点
m = new Node<T>;
m->data = s[i++];
p->lchild = m;
queue[rear++] = m;
if(s[i] != "#"){ //右
m = new Node<T>;
m->data = s[i++];
p->rchild = m;
queue[rear++] = m;
}else{
i++;
p->rchild = NULL;
}
}else{
i++;
p->lchild = NULL;
if(s[i] == "#"){
p->rchild = NULL;
i++;
}else{
m = new Node<T>;
m->data = s[i++];
p->rchild = m;
queue[rear++] = m;
}
}
//cout<<"----"<<i<<endl;
}
}
//析构函数调用
template <class T>
void BiTree<T>::releaseTree(Node<T> *root){
if(root){
//递归删除,先释放左右结点在根结点,不能是先根在左右
releaseTree(root->lchild);
releaseTree(root->rchild);
delete root;
}
}
//前序遍历二叉树(非递归)
//它是在进栈的时候输出
template <class T>
void BiTree<T>::nrPreOrder(Node<T> *root){
Node<T>* stack[SIZE];
int top = 0;
Node<T>* t = root;
while(t || top != 0){
//一直遍历左孩子,直到左孩子为空
while(t){
cout<<t->data<<" ";
stack[top++] = t;
t = t->lchild;
}
//如果左孩子为空了,然后出栈,遍历右结点
if(top != 0){
t = stack[--top];
t = t->rchild;
}
}
}
//中序遍历二叉树(非递归)
//它是在出栈的时候输出
template <class T>
void BiTree<T>::nrInOrder(Node<T> *root){
Node<T>* stack[SIZE];
int top = 0;
Node<T>* t = root;
while(t || top != 0){
//一直遍历左孩子,直到左孩子为空
while(t){
stack[top++] = t;
t = t->lchild;
}
//如果左孩子为空了,然后出栈,遍历右结点
if(top != 0){
t = stack[--top];
cout<<t->data<<" ";
t = t->rchild;
}
}
}
//后序遍历二叉树(非递归)
//需要设置标志位,
template <class T>
void BiTree<T>::nrPostOrder(Node<T> *root){
Node<T>* stack[SIZE];//存储树结点
int flag[SIZE]; //标记结点(0标记第一次出栈(未出),1标记第二次出栈(可出))
int top = 0;
Node<T>* t = root;
while(t || top != 0){
while(t){
stack[top] = t;
flag[top] = 0;
top++;
t = t->lchild;
}
//可以出栈
while(top != 0 && flag[top-1] == 1){
t = stack[--top];
cout<<t->data<<" ";
}
if(top != 0){//第一次出栈(实际上并未出栈,只是标志位变为0)
t = stack[top-1];
flag[top-1] = 1;
t = t->rchild;
}else{
t = NULL; //这是结束条件,当top==0,在置t为空才能结束整个循环
}
}
}
//搜索节点x
template <class T>
void BiTree<T>::search(Node<T>* root, T x, Node<T>* &p){
if(root){
if(root->data == x){ p = root; return; }
search(root->lchild, x, p);
search(root->rchild, x, p);
}
}
//寻找值为data的节点
template <class T>
Node<T>* BiTree<T>::searchNode(T x){
Node<T>* r = NULL;
if(root){
search(root, x, r);
}
return r;
}
//二叉树深度
template <class T>
int BiTree<T>::biTreeDepth(Node<T>* root){
int hl, hr;
if(root){
hl = biTreeDepth(root->lchild);
hr = biTreeDepth(root->rchild);
return (hl>hr?hl:hr)+1;
}
return 0;
}
//如果存在值为data的节点,返回其父节点
template <class T>
Node<T>* BiTree<T>::getParent(Node<T>* root, T x){
Node<T> *q;
if(!root) return NULL;
else{
//如果找到则返回
if((root->lchild && root->lchild->data==x) || (root->rchild && root->rchild->data==x)){
return root;
//如果递归左子树没找到,到右子树上找(把递归当成普通的操作一样)
}
//如果左子树找到了返回双亲
if(q = getParent(root->lchild, x)) return q;
//如果左子树没找到,则到右子树找
else if(q = getParent(root->rchild, x)) return q;
//如果左右都没找到则返回空
else return NULL;
}
}
//获取左兄弟
template <class T>
Node<T>* BiTree<T>::getLeftSibling(Node<T>* root, T x){
if(!root) return NULL;
else{
if(root->lchild && root->lchild->data==x){//如果左子树是x,则必然不存在左兄弟
return NULL;
}
if(root->rchild && root->rchild->data==x){//如果右子树是x,看是否存在左子树,存在则返回
return root->lchild;
}
return getLeftSibling(root->lchild, x);
return getLeftSibling(root->rchild, x);
}
}
//获取右兄弟
template <class T>
Node<T>* BiTree<T>::getRightSibling(Node<T>* root, T x){
if(!root) return NULL;
else{
if(root->rchild && root->rchild->data==x){
return NULL;
}
if(root->lchild && root->lchild->data==x){//获取右兄弟,则只需要关心左节点,如果左节点是x,在看左
return root->rchild;
}
return getRightSibling(root->lchild, x);
return getRightSibling(root->rchild, x);
}
}
//叶子个数
template <class T>
int BiTree<T>::leafCount(Node<T>* root){
if(!root) return 0;
else if(!root->lchild && !root->rchild){//如果是叶子节点,返回1
return 1;
}else{//如果左右子树不空,则递归遍历左右子树,结果就是左右子树叶子节点的和(递归加)
return leafCount(root->lchild) + leafCount(root->rchild);
}
}
//节点个数
template <class T>
int BiTree<T>::nodeCount(Node<T>* root){
if(!root) return 0;
else{//递归调用加
//节点个数=左子树节点个数+右子树节点个数+1
return nodeCount(root->lchild) + nodeCount(root->rchild) + 1;
}
}
template <class T>
void BiTree<T>::deleteSibTree(Node<T>* root, T x){
Node<T>* p;
if(root){
if(root->lchild && root->lchild->data==x){
p = root->lchild;
root->lchild = NULL;
//递归删除p的左右子树
releaseTree(p->lchild);
releaseTree(p->rchild);
delete p;
}else if(root->rchild && root->rchild->data==x){
p = root->rchild;
root->rchild = NULL;
//递归删除p的左右子树
releaseTree(p->lchild);
releaseTree(p->rchild);
delete p;
}else{//递归查找左右子树
deleteSibTree(root->lchild, x);
deleteSibTree(root->rchild, x);
}
}
}
3.测试程序
//二叉树的主函数,文件名为bitreemain.cpp
#include<iostream>
#include<string>
#include"bitree.cpp"
using namespace std;
int main()
{
//注:BiTree<string> bt在输入的时候,必须是按照前序遍历的顺序输入节点,因为递归建立树的过程就是前序遍历
//BiTree<string> bt;
//string a[] = {"a", "b", "c", "#", "d", "#", "#", "#", "#", "#", "#"};
//string a[] = {"a", "b", "c", "d", "#", "#", "#", "#", "#"};
string a[] = {"a", "b", "c", "#", "d", "e", "f", "g", "#", "#", "#", "#", "#", "#", "#"};
BiTree<string> bt(a, 15); //创建一棵树(按层序输入)
Node<string>* root = bt.getRoot( ); //获取指向根结点的指针
cout<<"------前序遍历------ "<<endl;
bt.preOrder(root);
cout<<endl;
cout<<"------中序遍历------ "<<endl;
bt.inOrder(root);
cout<<endl;
cout<<"------后序遍历------ "<<endl;
bt.postOrder(root);
cout<<endl;
cout<<"------层序遍历------ "<<endl;
bt.leverOrder(root);
cout<<endl;
cout<<"------非递归前序遍历------ "<<endl;
bt.nrPreOrder(root);
cout<<endl;
cout<<"------非递归中序遍历------ "<<endl;
bt.nrInOrder(root);
cout<<endl;
cout<<"------非递归后序遍历------ "<<endl;
bt.nrPostOrder(root);
cout<<"\n---------基本操作----------"<<endl;
Node<string>* t;
cout<<"搜索b:";
t = bt.searchNode("b");
if(t) cout<<t->data<<endl;
else cout<<"无"<<endl;
if(t->lchild) cout<<"L:"<<t->lchild->data<<endl;
if(t->rchild) cout<<"R:"<<t->rchild->data<<endl;
cout<<"a的双亲:";
t = bt.getParent(root, "a");
if(t) cout<<t->data<<endl;
else cout<<"无"<<endl;
cout<<"b的双亲:";
t = bt.getParent(root, "b");
if(t) cout<<t->data<<endl;
else cout<<"无"<<endl;
cout<<"b的左兄弟:";
t = bt.getLeftSibling(root, "b");
if(t) cout<<t->data<<endl;
else cout<<"无"<<endl;
cout<<"b的右兄弟:";
t = bt.getRightSibling(root, "b");
if(t) cout<<t->data<<endl;
else cout<<"无"<<endl;
cout<<"c的左兄弟:";
t = bt.getLeftSibling(root, "c");
if(t) cout<<t->data<<endl;
else cout<<"无"<<endl;
cout<<"c的右兄弟:";
t = bt.getRightSibling(root, "c");
if(t) cout<<t->data<<endl;
else cout<<"无"<<endl;
cout<<"二叉树深度:"<<bt.biTreeDepth(root)<<endl;
cout<<"叶子个数:"<<bt.leafCount(root)<<endl;
cout<<"节点个数:"<<bt.nodeCount(root)<<endl;
cout<<"删除节点为d的子树"<<endl;
bt.deleteSibTree(root, "d");
cout<<"------前序遍历------ "<<endl;
bt.preOrder(root);
cout<<endl;
cout<<"------中序遍历------ "<<endl;
bt.inOrder(root);
cout<<endl;
return 0;
}
4.总结
每个操作的一些注意事项都在代码中进行了说明,主要注意一下,建立二叉树的时候,如何输入节点?见下图: