二叉树的存储与遍历


结点的度:结点所拥有的子树棵数。
结点的层次:从根到该结点所经路径上的分支条数。
叶结点:度为0的结点。
树的深度:树中距离根结点最远的结点所处层次即为树的深度。
树的度:树中结点的度的最大值。
有序树:树中结点的各棵子树是有次序的。

二叉树

特点:每个结点最多有两个子女,并且二叉树的子树有左、右之分,其子树的次序不能颠倒,因此是有根有序树。

二叉树的性质

  • 在二叉树的第i(i>=1)层最多有2^(i-1)个结点。
  • 深度为k(k>=0)的二叉树,最少有k个结点,最多有2^k-1个结点。
  • 对任何一棵非空二叉树,如果其叶结点数为n0,度为2的非叶结点数为n2,则n0=n2+1。
  • 完全二叉树:当深度为k时,从第1层到第k-1层的所有各层的结点数都是满的,只有最下面第k层或是满的,或从右向左连续缺若干结点。
  • 具有n个结点的完全二叉树的深度为log2^(n+1)取上。
  • 如果将一棵有n个结点的完全二叉树自顶向下,同一层自左向右连续给结点编号。则有如下性质:
    1. 若i==1,则结点i为根;若i>1,则结点i的父节点为结点i/2取下。
    2. 若2i <= n,则结点i的左子女结点2i。
    3. 若2i+1<=n,则结点i的右子女为结点2i+1。
    4. 若结点编号i为奇数,且i!=1,它处于右兄弟位置。
    5. 若结点编号i为偶数,且i!=n,它处于左兄弟位置。
    6. 结点i所处层次为log2^i取下再加1。

二叉树的存储表示

数组存储表示

适用于二叉树的大小和形态不发生剧烈动态变化的场合,例如完全二叉树。

链表存储表示

二叉树的结点至少有三个域:数据data、左子女结点指针、右子女结点指针。要找结点的父结点可以增加一个父指针域。

二叉树遍历及其应用

L、R、V分别代表遍历一个结点的左子树、右子树和该结点。

三种遍历方式:

//前序遍历VLR
template<class T>
void BinaryTree<T>::PreOreder(BinTreeNode<T> *subTree, void(*visit)(BinTreeNode<T> *p)){//前序遍历以subTree为根的子树,第二个参数是访问函数visit名当指针传入。
	if(subTree !=NULL){
		visit(subTree);//访问根结点
		PreOrder(subTree->leftChild,visit);
		PreOrder(subTree->right,visit);
	}
};
	
//中序遍历算法
template<class T>
void BinaryTree<T>::InOreder(BinTreeNode<T> *subTree, void(*visit)(BinTreeNode<T> *p)){
	if(subTree !=NULL){
		InOrder(subTree->leftChild,visit);
		visit(subTree);//访问根结点
		InOrder(subTree->right,visit);
	}
};

//后续遍历
template<class T>
void BinaryTree<T>::PostOreder(BinTreeNode<T> *subTree, void(*visit)(BinTreeNode<T> *p)){
	if(subTree !=NULL){
		PostOrder(subTree->leftChild,visit);
		PostOrder(subTree->right,visit);
		visit(subTree);//访问根结点
	}
};

利用二叉树前序遍历建立和输出二叉树

//前序遍历建立二叉树的算法
template<class T>
void BinaryTree<T>::CreateBinTree(ifstream& in, BinTreeNode<T> *& subTree){
	T item;
	if(!in.eof()){//未读完,读入并建树
		in>>item;//读入根结点
		if(item!=RefValue){//不是终止值
			subTree = new BinTreeNode<T>(item);
			if(subTree==NULL){
				cerr<<"存储分配错"<<endl;
				exit(1);}
			CreateBinTree(in, subTree->leftChild);
			CreateBinTree(in, subTree->rightChild);
			}
		else subTree = NULL;//建立空子树结束递归
	}
}

//以广义表形式输出二叉树的算法
#include “binaryTree.h”
template<class T>
void PrintBTree(BinTreeNode<T> *BT){
	if(BT != NULL){
		cout<<BT->data;
		if(BT->leftChild != NULL || BT->rightChild != NULL){
			cout<<'(';
			PrintBTree(BT->leftChild);
			cout<<',';
			if(BT->rightChild != NULL)
				PrintBTree(BT->rightChild);
			cout<<')';
		}
	}
}

二叉树遍历的非递归算法

利用栈记录遍历时的回退路径

//前序遍历,访问一个结点后向左子树遍历下去,利用栈记录该结点的右子女。
#include"stack.h"
template<class T>
void BinaryTree<T>::PreOrder(void(*visit)(BinTreeNode<T>*p)){
	stack<BinTreeNode<T>*> S;//声明一个栈,栈的元素是指向结点的指针
	BinTreeNode<T> *p = root;
	S.Push(NULL);//压入NULL,设置为遍历结束条件
	while(p != NULL){
		visit(p);
		if(p->rightChild != NULL) S.push(p->rightChild);
		if(p->leftChild !+ NULL) p=p->leftChild;
		else S.Pop(p);//将栈顶元素弹出赋给p
	}
}
//另一种前序遍历的非递归算法
#include"stack.h"
template<class T>
void BinaryTree<T>::PreOrder(void(*visit)(BinTreeNode<T>*p)){
	stack<BinTreeNode<T>*> S;
	BinTreeNode<T> *p;
	S.Push(root);//将根结点压入栈中
	while(! S.IsEmpty()){
		S.Pop(p);visit(p);//退栈,访问
		if(p->rightChild != NULL) S.push(p->rightChild);//先压入右子树再左子树,退栈访问刚好相反
		if(p->leftChild != NULL) S.push(p->leftChild);
	}
}

//中序遍历非递归算法
#include"stack.h"
template<class T>
void BinaryTree<T>::InOrder(void(*visit)(BinTreeNode<T>*p)){
	stack<BinTreeNode<T>*> S;
	BinTreeNode<T> *p = root;
	do{
		while(p!=NULL){//遍历到最左下的结点
			S.Push(p);//该子树沿途结点进展,其实把父结点和左子女都压入了
			p=p->leftChild;
		}
		if(!S.IsEmpty()){
			S.Pop(p);visit(p);
			p=p->rightChild;//转到右子女结点
		}
	}while(p != NULL || !S.IsEmpty());//结束条件是栈为空同时遍历指针也为空,若栈为空,而指针不为空说明右子树非空。
}

//后续遍历所用的栈的结点定义
template<class T>
struct stkNode{
	BinTreeNode<T> *ptr;
	enum tag{L,R};//栈暂存根结点,当向左子树遍历下去,tag为L,当向右子树遍历为R,从右子树退出时才访问位于栈顶的根结点的值。
	sktNode(BinTreeNode<T> *N = NULL):ptr(N),tag(L){}
}

后续遍历的非递归算法
#include "stack.h"
template<class T>
void BinaryTree<T>::PostOrder(void(*visit)(BinTreeNode<T>*p)){
	Stack<sktNode<T>> S;stkNode<T> w;
	BinTreeNode<T> *p=root;
	do{
		while(p!=NULL){
			w.ptr=p;w.tag=L;S.Push(w);
			p=p->leftChild;
		}
		int continue1=1;//
		while(continue1 && !S.IsEmpty()){
			S.Pop(w);p=w.ptr;
			switch(w.tag){
				case L:
					w.tag=R;S.Push(w);//从左子树退回,修改栈顶tag
					continue1=0;
					p=p->rightChild;
					break;//这里是对应switch,否则不会自动停止,会继续执行下面的case
				case R: visit(p); break;
			}
		}
	}while(!S.IsEmpty()):
	cout<<endl;
}

由给定的前序序列和中序序列能够唯一确定一棵二叉树。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值