您的位置:首页 > 理论基础 > 数据结构算法

二叉树学习笔记-深度和宽度

2015-08-19 11:05 465 查看
二叉树这种数据结构的重要性不必多言,下面计算二叉树的深度和宽度。要计算二叉树的深度和宽度,先构造一课二叉树,为了方便验证结果的正确性,在这里构造二叉搜索树。二叉树由节点构成,所以先实现节点类:

template <typename T>
class stnode{
public:
T nodeValue;//节点值
stnode<T> *left,*right,*parent;//依次是指向当前节点的左子结点、右子节点、父节点的指针
stnode(const T& item);//构造函数
};
template <typename T>//初始化nodeValue的值为item,三个指针均为NULL
stnode<T>::stnode(const T& item=T()):nodeValue(item){
left=NULL;
right=NULL;
parent=NULL;
}


先说计算深度,这里引用一种<<数据结构C++语言描述--应用STL模板库>>这本书中的计算方法,采用的是递归的思想,即采用后序遍历二叉树,分别计算左子节点和右子节点的深度,取其中较大值加上1就得到自己的深度。比如下面的二叉树



计算A节点的深度首先计算左右子节点BC的深度,同理计算B节点的深度先计算左右子节点DE的深度。定义一个NULL节点的深度为-1,这样一个叶节点的深度就为0(取左右子节点的深度,均为-1,再加上1)。这样D节点的左右子节点的深度分别为-1和0,取较大值加上1,那么D的深度就为1,B的左右子节点的深度分别为1和0,取较大值加上1,B的深度就为2,。这样一层一层递归到根节点就得出了整个二叉树的深度。

int dep(stnode<int>* root){
int leftDep,rightDep,totalDep;
if(root==NULL)
return -1;	//定义一个NULL节点的深度为-1
else{			//采用后序遍历的方式,先计算左子节点的深度,再计算右子节点的深度
leftDep=dep(root->left);
rightDep=dep(root->right);
totalDep=1+(leftDep>rightDep?leftDep:rightDep);//取左右子节点的深度值中较大值加上1就为自身的深度
}
return totalDep;//返回二叉树的深度
}
测试一下是否正确

void main(){
unsigned int i;
stree<int> tree(5);
tree.insert(4);
tree.insert(3);
tree.insert(6);
cout<<dep(tree.root)<<endl;
while(1);
}


因为是二叉搜索树,插入值以后树的结构是明确的,插入以后的树如下图



树的深度为2,看一下结果



另一种方法是非递归实现,树的遍历输出除了前中后序遍历输出还可以按层次输出,如下图所示



就像楼层一样是一层一层的,这种输出的思想就是用一个队列,先将根节点插入队列。随后循环取出队列中的所有元素,每取出一个元素,输出它的节点值,然后将它的左右子节点再插入到队列,这样当队列为空时,就遍历完了整个树。那么如果用两个队列,如上图所示,将p中的所有元素全部取出,每取出一个元素就把它的左右子节点全部插入到q队列,此时p为空。同理再将q中的全部元素取出,插入到p中,这样循环,一直到pq均为空,树遍历结束。如果每次循环记录下pq的size,取size的最大值就可以得到树的宽度,统计pq互相插值的次数,就可以得到“楼层数”,这个楼层数就是我们需要的深度。用代码实现上面的思想,先计算深度

<pre name="code" class="cpp">int depth(stnode<int>* root){
int depth=-1;				//和声明一个空树的深度为-1一样
queue<stnode<int> *> p,q;	//声明两个队列
stnode<int> *temp;			//临时节点
p.push(root);				//将根节点插入到p中
while(!(p.empty()&&q.empty())){	//当pq均为空时退出整个循环,表示树遍历结束
while(!p.empty()){		//取出p中的每个元素,然后将它不为空的左右子节点插入到q中
temp=p.front();
p.pop();
if(temp->left!=NULL)
q.push(temp->left);
if(temp->right!=NULL)
q.push(temp->right);
}
depth++;				//p往q中插值进行了一次,深度加1
/*有时候p恰好是最后一层,即取出的元素的左右子节点均为空,这时q中没有元素,那么就跳出循环,
否则下面的depth++会多执行一次,没有在上面的while循环前面加这句话的原因是:下面的while循环
取出了q中的所有元素,所以q必为空,此时如果p也为空,那么就会跳出整个大的while循环*/
if(q.empty())
break;
while(!q.empty()){		//取出q中的每个元素,将它的左右子节点插入到p中
temp=q.front();
q.pop();
if(temp->left!=NULL)
p.push(temp->left);
if(temp->right!=NULL)
p.push(temp->right);
}
depth++;				//q往p中插值进行了一次,深度加1
}
return depth;
}



测试一下两种计算深度方法的正确性

void main(){
unsigned int i;
stree<int> tree(50);
for(i=0;i<100;i++)
tree.insert(rand()%100);
cout<<dep(tree.root)<<endl;
cout<<depth(tree.root)<<endl;
while(1);
}
将0-99以内的100个随机树插入到树中,用两种方法计算深度



均为11。

按照这种思想,如果记录p和q的size值,取出其中的最大值就是要计算的宽度,用代码实现这种思想

int width(stnode<int>* root){
int width=0;				//宽度
queue<stnode<int> *> p,q;	//声明两个队列
stnode<int> *temp;			//临时节点
p.push(root);				//将根节点插入到p中
while(!(p.empty()&&q.empty())){			//pq循环互相差值
if(p.size()>width)		//记录p的size,如果大于width,那么就将p.size()赋值给width
width=p.size();
while(!p.empty()){
temp=p.front();
p.pop();
if(temp->left!=NULL)
q.push(temp->left);
if(temp->right!=NULL)
q.push(temp->right);
}
if(q.size()>width)		//记录q的size,如果大于width,那么就将q.size()赋值给width
width=q.size();
while(!q.empty()){
temp=q.front();
q.pop();
if(temp->left!=NULL)
p.push(temp->left);
if(temp->right!=NULL)
p.push(temp->right);
}

}
return width;				//这样,width中存放的是pq的size值中的最大值,也就是二叉树的宽度
}
测试一下

void main(){
unsigned int i;
stree<int> tree(50);
tree.insert(40);
tree.insert(52);
tree.insert(38);
tree.insert(41);
tree.insert(51);
tree.insert(60);
cout<<width(tree.root)<<endl;
while(1);
}
这样插值后的宽度为4,看一下结果



结果正确。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息