数和二叉树(二)
二叉树的存储结构及实现
顺序存储结构
二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置(下标)应能体现结点之间的逻辑关系——父子关系。
利用数组下标来反映结点之间的逻辑关系:
完全二叉树和满二叉树中结点的序号可以唯一地反映出结点之间的逻辑关系 。
二叉链表
基本思想:令二叉树的每个结点对应一个链表结点,链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左右孩子的指针。
template <class T>
struct BiNode
{
T data;
BiNode<T> *lchild, *rchild;
};
(具有n个结点的二叉链表中,有n+1个空指针。)
二叉链表存储结构的类声明:
template <class T>
class BiTree
{
public:
BiTree();
~BiTree( );
void PreOrder(){PreOrder(root);}
void InOrder() {InOrder(root);}
void PostOrder() {PostOrder(root);}
void LevelOrder(){LeverOrder(root)};
private:
BiNode<T> *root;
BiNode<T> * Creat( );
void Release(BiNode<T> *root);
void PreOrder(BiNode<T> *root);
void InOrder(BiNode<T> *root);
void PostOrder(BiNode<T> *root);
void LevelOrder(BiNode<T> *root);
};
前序遍历——递归算法
template <class T>
void BiTree::PreOrder(BiNode<T> *root)
{
if (root ==NULL) return;
else {
cout<<root->data;
PreOrder( );
PreOrder( );
}
}
前序遍历——非递归算法
二叉树前序遍历的非递归算法的关键:
在前序遍历过某结点的整个左子树后,如何找到该结点的右子树的根指针。
解决办法:在访问完该结点后,将该结点的指针保存在栈中,以便以后能通过它找到该结点的右子树。
栈是实现递归的最常用的结构
前序遍历——非递归算法(伪代码)
1.栈s初始化(空栈);
2.循环直到root为空且栈s为空
2.1 当root不空时循环
2.1.1 输出root->data;
2.1.2 将指针root的值保存到栈中;
2.1.3 继续遍历root的左子树(root=root->lchild)
2.2 如果栈s不空,则
2.2.1 将栈顶元素弹出至root(root=s.pop());
2.2.2 准备遍历root的右子树(root=root->rchild);
template <class T>
void BiTree::PreOrder(BiNode<T> *root) {
SeqStack<BiNode<T> *> s;
while (root!=NULL | | !s.empty()) {
while (root!= NULL) {
cout<<root->data;
s.push(root);
root=root->lchild;
}
if (!s.empty()) {
root=s.pop();
root=root->rchild;
}
}
}
二叉树的建立
遍历是二叉树各种操作的基础,可以利用遍历的思想完成二叉树的各种操作,例如建立一棵二叉树。
设二叉树中的结点均为一个字符。假设扩展二叉树的前序遍历序列由键盘输入,root为指向根结点的指针,二叉链表的建立过程是:
1,按扩展前序遍历序列输入结点的值
2,如果输入结点值为“#”,则建立一棵空的子树
3,否则,根结点申请空间,将输入值写入数据域中,
4,以相同方法的创建根结点的左子树
5,以相同的方法创建根结点的右子树递归方法
中序遍历——递归算法
template <class T>
void BiTree::InOrder (BiNode<T> *root)
{
if (root==NULL) return;
else {
InOrder(root->lchild);
cout<<root->data;
InOrder(root->rchild);
}
}
中序遍历——非递归算法(伪代码)
1.栈s初始化(空栈);
2.循环直到root为空且栈s为空
2.1 当root不空时循环
2.1.1 将指针root的值保存到栈中;
2.1.2 继续遍历root的左子树(root=root->lchild)
2.2 如果栈s不空,则
2.2.1 将栈顶元素弹出至root(root=s.pop());
2.2.2 输出root->data;
2.2.3 准备遍历root的右子树(root=root->rchild);
template <class T>
void BiTree::InOrderwithoutD (BiNode<T> *root)
{
stack< BiNode<T> * > aStack;
while(!aStack.empty()||root) {
while(root){
aStack.push(root);
root=root->lchild;
}
if(!aStack.empty()){
root=aStack.top();
aStack.pop();
cout<<root->data;
root=root->rchild;
}
}
}
后序遍历——递归算法
template <class T>
void BiTree::PostOrder(BiNode<T> *root)
{
if (root==NULL) return;
else {
PostOrder(root->lchild);
PostOrder(root->rchild);
cout<<root->data;
}
}
非递归后序遍历二叉树
算法分析
①定义一个栈;从根节点出发开始遍历,p=root,如果,root==NULL, 不进行遍历;无条件进行下面的工作:如果指针不空,指针打上left标记,并将指针进栈,执行②;否则,执行③
②p=p->lchild,重复①
③栈顶元素出栈P
④查看P的标志,如果标志为right,进行下面的工作,否则,执行⑤
1)访问当前节点P
2)如果栈空 ,算法结束;
3)否则,栈顶元素出栈,转④
⑤修改P的标志,让P重新入栈,p=P->rchild,执行2
非递归后序遍历二叉树
栈中的元素类型定义StackElement
enum Tags{Left,Right}; //特征标识定义
template <class T>
class StackElement //栈元素的定义
{
public:
BiTreeNode<T>* pointer; //指向二叉树结点的指针
Tags tag; //特征标识申明
};
层序遍历
1.队列Q初始化;
2. 如果二叉树非空,将根指针入队;
3. 循环直到队列Q为空
3.1 q=队列Q的队头元素出队;
3.2 访问结点q的数据域;
3.3 若结点q存在左孩子,则将左孩子指针入队;
3.4 若结点q存在右孩子,则将右孩子指针入队;
#include <queue>
using namespace std;
template<class T>
void BiTree<T>::LevelOrder(BinaryTreeNode<T>* root){
queue<BiTreeNode<T>*> aQueue;
if(root)
aQueue.push(root);
while(!aQueue.empty())
{
root=aQueue.front(); //取队列首结点
aQueue.pop();
cout<<pointer->data;//访问当前结点
if(root->lchild) //左子树进队列
aQueue.push(root->lchild);
if(root->rchild) //右子树进队列
aQueue.push(root->rchild);
}//end while
}
二叉树 的析构
template<class T>
void BiTree<T>::Release(BiNode<T>* root){
if (root != NULL){
Release(root->lchild); //释放左子树
Release(root->rchild); //释放右子树
delete root;
}
}
template<class T>
BiTree<T>::~BiTree(void)
{
Release(root);
}
树中节点的数目
template<class T>
int BiTree<T>::count(BiNode<T>* root){
int number=0;
if (root==NULL)
number=0;
else
number=count(root->lchild)+count(root->rchild)+1;
return number;
}
计算树的高度
高度的定义
max(左子树高度,右子树高度)+1
算法分析
从根节点出发开始计算,
如果root==NULL, 高度为0;
否则,分别计算左子树的高度;右子树的高度;返回max(左子树高度,右子树高度)+1
递归的定义
template<typename T>
int BiTree<T>::cal_height(BiTreeNode<T> * root){
int lheight=0,rheight=0;
if (root==0) return 0;
输出中缀表达式。并加上相应的括号
(a+(b*(c-d)))-(e/f)
基本思想
中序遍历。
中序遍历左子树前,输出左括号
中序遍历右子树后,输出右括号
如果遍历叶子结点的左右子树,不输出括号
如果遍历根节点的左右子树,不输出括号(否则,会得到形如(a+b)的表达式)
void BiTree<T>::In_Expression(BiNode<T>* root){
if(root)
{
if(root!=this->root&&root->lchild!=0&&root->rchild!=0)
cout<<"(";
In_Expression(root->lchild);
cout<<root->data;
In_Expression(root->rchild);
if(root!=this->root&&root->lchild!=0&&root->rchild!=0)
cout<<")";
}
}
计算二叉树的宽度
所谓宽度是指在二叉树的各层上,具有结点数最多的那一层上的结点总数
struct q_element{ BiNode * root; int level;};
int BiTree::Width(){
queue< struct q_element > q;
int num[100]={0,1};
q_element s,child;
BiNode *root;
root=this->root;
if(root==NULL)
return 0;
s.root=root; s.level=1; q.push(s);
while(!q.empty()) {
s=q.front();
if(s.root->lchild){
num[s.level+1]++;
child.root=s.root->lchild;
child.level=s.level+1;
q.push(child);
}
if(s.root->rchild) {
num[s.level+1]++;
child.root=s.root->rchild;
child.level=s.level+1;
q.push(child);
}
q.pop();
}
int max=0,i=1;
while(num[i]>0){
if(max<num[i])
max=num[i];
i++;
}
return max;
}