二叉树作为树的一种,是一种重要的数据结构
常见类型:创建一颗二叉树(先序,中序,后序)、遍历一颗二叉树(先序,中序,后序和层次遍历)、求二叉树中叶子节点的个数、求二叉树的高度、求二叉树中两个节点的最近公共祖先、打印和为某一值的全部路径、求某一节点是否在一个树中等等。
不妨先看一下几种常见的二叉树:
完全二叉树:若二叉树的高度是h,除第h层之外,其他(1~h-1)层的节点数都达到了最大个数,并且第h层的节点都连续的集中在最左边。想到点什么没?实际上,完全二叉树和堆联系比较紧密哈~~~
满二叉树:除最后一层外,每一层上的所有节点都有两个子节点,最后一层都是叶子节点。
结点类定义:
//二叉树的节点类
class BinTreeNode
{
private:
int data;
BinTreeNode *left,*right; //定义左右子结点
public:
//利用初始化列表完成data,left,rightn的初始化
BinTreeNode(const int &item,BinTreeNode *lPtr = NULL,BinTreeNode *rPtr = NULL):data(item) ,left(lPtr),right(rPtr){};
void set_data(int item) //插入数据
{
data = item;
}
int get_data()const //返回数据
{
return data;
}
void set_left(BinTreeNode *l) //设置左子树
{
left = l;
}
BinTreeNode* get_left()const //返回左子树
{
return left;
}
void set_right(BinTreeNode *r) //设置右子树
{
right = r;
}
BinTreeNode* get_right()const //返回右子树
{
return right;
}
};
二叉树原型: 包含下面申明的所有函数
//二叉树
class BinTree
{
private:
BinTreeNode *root;
public:
BinTree(BinTreeNode *t = NULL):root(t){}; //初始化列表的构造函数
~BinTree(){delete root;};
void set_root(BinTreeNode *t) //设置根节点为形参里面的节点
{
root = t;
}
BinTreeNode* get_root()const //返回根节点
{
return root;
}
//1.创建二叉树
BinTreeNode* create_tree();
//2.前序遍历
void pre_order(BinTreeNode *r)const;
//3.中序遍历
void in_order(BinTreeNode *r)const;
//4.后序遍历
void post_order(BinTreeNode *r)const;
//5.层次遍历
void level_order(BinTreeNode *r)const;
//6.获得叶子节点的个数
int get_leaf_num(BinTreeNode *r)const;
//7.获得二叉树的高度
int get_tree_height(BinTreeNode *r)const;
//8.交换二叉树的左右儿子
void swap_left_right(BinTreeNode *r);
//9.求两个节点pNode1和pNode2在以r为树根的树中的最近公共祖先
BinTreeNode* get_nearest_common_father(BinTreeNode *r,BinTreeNode *pNode1,BinTreeNode *pNode2)const;
//10.打印和为某一值的所有路径
void print_rout(BinTreeNode *r,int sum)const;
//11.判断一个节点t是否在以r为根的子树中
bool is_in_tree(BinTreeNode *r,BinTreeNode *t)const;
};
创建一颗二叉树
创建一颗二叉树,可以创建先序二叉树,中序二叉树,后序二叉树。我们在创建的时候为了方便,不妨用‘#’表示空节点,这时如果先序序列是:6 4 2 3 # # # # 5 1 # # 7 # #,那么创建的二叉树如下:
//创建二叉树,这里不妨使用前序创建二叉树,遇到‘#’表示节点为空
BinTreeNode* BinTree::create_tree()
{
char item; //定义一个字符串item
BinTreeNode *t,*t_l,*t_r; //定义结点还有左右结点
cin>>item; //输入字符串
if(item != '#') //若字符串输入正确
{
BinTreeNode *pTmpNode = new BinTreeNode(item-48); //new一个节点空间
t = pTmpNode; //并让在开头定义的t=上一部的结点
t_l = create_tree(); //在t的左边新建一棵树
t->set_left(t_l); //不懂???
t_r = create_tree();
t->set_right(t_r);
return t;
}
else
{
t = NULL;
return t;
}
}
二叉树的遍历
二叉树的遍历分为:先序遍历,中序遍历和后序遍历,这三种遍历的写法是很相似的,利用递归程序完成也是灰常简单的:
//前序遍历
void BinTree::pre_order(BinTreeNode *r)const //形参中给的是根节点
{
BinTreeNode *pTmpNode = r; //将根节点赋给pTmpNode
if(pTmpNode != NULL)
{
cout<<pTmpNode->get_data()<<" "; //前序就是前访问
pre_order(pTmpNode->get_left()); //在再左子树访问
pre_order(pTmpNode->get_right()); //递归调用
}
}
//中序遍历
void BinTree::in_order(BinTreeNode *r)const
{
BinTreeNode *pTmpNode = r;
if(pTmpNode != NULL)
{
in_order(pTmpNode->get_left());
cout<<pTmpNode->get_data()<<" ";
in_order(pTmpNode->get_right());
}
}
//后序遍历
层次遍历
层次遍历也是二叉树遍历的一种方式,二叉树的层次遍历更像是一种广度优先搜索(BFS)。因此二叉树的层次遍历利用队列来完成是最好不过啦,当然不是说利用别的数据结构不能完成。
//层次遍历
void BinTree::level_order(BinTreeNode *r)const
{
if(r == NULL)
return;
deque<BinTreeNode*> q; //定义一个双端队列q
q.push_back(r); //将r放入队列q中
while(!q.empty()) //一直循环到队列中没有元素
{
BinTreeNode *pTmpNode = q.front(); //将第一个队列中的元素赋给pTmpNode节点
cout<<pTmpNode->get_data()<<" "; //输出结果
q.pop_front(); //将刚才那个第一个元素退出
if(pTmpNode->get_left() != NULL)
{
q.push_back(pTmpNode->get_left()); //如果左不为空,则再加入
}
if(pTmpNode->get_right() != NULL) //如果右不为空,则再加入
{
q.push_back(pTmpNode->get_right());
}
}
}
求二叉树中叶子节点的个数
树中的叶子节点的个数 = 左子树中叶子节点的个数 + 右子树中叶子节点的个数。利用递归代码也是相当的简单,易懂。
//获取叶子节点的个数
int BinTree::get_leaf_num(BinTreeNode *r)const
{
if(r == NULL)//该节点是空节点,比如建树时候用'#'表示
{
return 0;
}
if(r->get_left()==NULL && r->get_right()==NULL)//该节点并不是空的,但是没有孩子节点
{
return 1;
}
//递归整个树的叶子节点个数 = 左子树叶子节点的个数 + 右子树叶子节点的个数
return get_leaf_num(r->get_left()) + get_leaf_num(r->get_right());
//就是1+1=2个叶子节点
}
求二叉树的高度
求二叉树的高度也是非常简单,不用多说:树的高度 = max(左子树的高度,右子树的高度) + 1 。
//获得二叉树的高度
int BinTree::get_tree_height(BinTreeNode *r)const
{
if(r == NULL)//节点本身为空
{
return 0;
}
if(r->get_left()==NULL && r->get_right()==NULL)
//叶子节点都不存在,只有根节点
{
return 1;
}
int l_height = get_tree_height(r->get_left());//??
int r_height = get_tree_height(r->get_right());
return l_height >= r_height ? l_height + 1 : r_height + 1;
}
判断一个节点是否在一颗子树中
可以和当前根节点相等,也可以在左子树或者右子树中。
//判断一个节点t是否在以r为根的子树中
bool BinTree::is_in_tree(BinTreeNode *r,BinTreeNode *t)const
{
if(r == NULL) //上面定义来看,r是根,t是目的节点
{
return false; //如果r为空,说明为空树
}
else if(r == t) //根节点就是要找的节点
{
return true;
}
else
{
bool has = false; //定义一个名为has的bool值为false
if(r->get_left() != NULL)
{
has = is_in_tree(r->get_left(),t); //开始左循环
}
if(!has && r->get_right()!= NULL) //开始右循环
{
has = is_in_tree(r->get_right(),t);
}
return has;
}
} //相当于是一个前序遍历的比较算法