数据结构-树

树中,每一层上的数据元素可能和下一层中的多个元素相关,但最多智能和上一层中的一个元素相关。一个根结点分出m个子树。各节点关系以下图为例:

A是B的双亲,B是A的子女,<A,B>是从A到B的边。
B、C为兄弟,但G和H不是兄弟;
路径:A-B-F为一条路径,要求不断从父节点到子结点,路径长度为2(经过的边的个数)。
根节点层数为0,以此类推。树中结点最大的层数成为树的深度,上图深度为2。
一个结点子女的个数称为该结点的度数。度数为0的结点称为树叶,又叫终端结点;度数大于0的结点叫做分支结点

二叉树

二叉树结点的子树有左子树、右子树之分,第i层结点数最多为2i
深度为k的二叉树最多有2k+1 -1 个结点。
满二叉树:深度为k,且有2k+1 -1 个结点。
完全二叉树:最多只允许最下面两层上结点度数可以小于2,且最小面一层的结点都集中再该层最左边若干位置上,如下图所示。

链式存储结构

存储二叉树最直接的方式是链式存储结构。每个结点存储自身值,和两个指针域lchild、rchild,分别指向左子女和右子女。

//先来一个二叉树结点类的定义
class Node
{
public:
	Node() :lchild(0), rchild(0){}          //类Node有两个构造函数,第一个建立了一个lchild和rchild均为空的结点
	Node(int val/*, Node *lptr, Node *rptr*/)  //第二个建立一个叶或非叶结点,并用val初始化data。
	{
		data = val;
		lchild = 0;
		rchild = 0;
	}
	int data;
	Node *lchild, *rchild;
};

或者更简单的如下:
struct BinaryNode
{
   int   m_nValue;
   BinaryNode *m_pleft;
   BinaryNode *m_pright;
}

二叉树的遍历

如果用D、L、R分别代表根结点、左子树、右子树。

先序遍历(DLR次序):若二叉树非空,访问根结点,先序遍历左子树,先序遍历右子树。
先输出根结点A,然后访问A的左子树,左子树根结点为B,输出B;
然后访问B的左子树根结点,输出D;D无左子树,遍历到右结点G,输出。
此时遍历完了B的左子树,继续B的右子树,输出E;
遍历完了A的左子树,继续A的右子树,根节点为C,输出;
C的左子树,输出F。 所以,最终输出 ABDGECF
中序遍历(LDR次序):若二叉树非空,依次,中序遍历左子树,访问根结点,中序遍历右子树。
不断划分左子树至D,没有左子树,输出父节点D,然后右结点G;
B的左子树结束,输出B,输出右结点E;
A的左子树结束,输出A,开始A的右子树;
C有左子树F,输出F,然后输出父节点C;
右子树遍历结束。最终输出 DGBEAFC
后序遍历(LRD次序):若二叉树非空,依次,后序遍历左子树,后序遍历右子树,访问根结点。
依旧是先不断找左子树,到D,没有左子树了,输出右子树G,然后是根结点D;
对B分出的左子树遍历完后,输出右子树E,然后是B;
A分出的左子树完成后,该右子树,C处只有左子树F,输出F后,输出D;
输出根结点A。 最终结果是: GDEBFCA

void Preorder(Node* ptr)
{
	if (ptr != 0)
	{
		cout << ptr->data<<" ";   //访问根结点
		Preorder(ptr->lchild);   //先序遍历左子树
		Preorder(ptr->rchild);   //先序遍历右子树
	}
}
void Inorder(Node *ptr)
{
	if (ptr != 0)
	{
		Inorder(ptr->lchild);
		cout<< ptr->data << " ";
		Inorder(ptr->rchild);
	}
}
void Postorder(Node *ptr)
{
	if (ptr != 0)
	{
		Postorder(ptr->lchild);
		Postorder(ptr->rchild);
		cout << ptr->data << " ";
	}
}

附加一段测试代码:

int main()
{
	Node *p1 = new Node(1);
	Node *p2 = new Node(2);
	Node *p3 = new Node(3);
	Node *p4 = new Node(4);
	Node *p5 = new Node(5);
	
	p1->lchild = p2;
	p1->rchild = p3;
	p2->lchild = p4;
	p2->rchild = p5;
	
	Preorder(p1);
	cout << endl;
	Inorder(p1);
	cout << endl;
	Postorder(p1);
	cout << endl;
	
	delete p1, p2, p3, p4, p5;
	system("pause");
	return 0;
}

二叉树遍历的应用

(1)计算二叉树的深度
通过后序遍历,可以先得到左子树的深度dl,然后得到右子树的深度dr,则二叉树的深度为1+max{dl,dr}。设空二叉树的深度为-1.
(2)计算二叉树结点的个数
在遍历过程中,每个结点都要访问一次,只要计算出过程中访问的结点个数即可。
(3)计算二叉树叶子结点的个数
在遍历过程中,若发现某个结点的左右子树皆为空,则该 结点为叶子结点。

/**********************二叉树遍历的应用*****************************/
//计算二叉树深度(使用后续遍历思想)
int Depth(Node* ptr)
{
	if (ptr == 0) return -1;
	int dl =Depth(ptr->lchild);
	int dr = Depth(ptr->rchild);
	return 1+(dl>dr ? dl:dr);
}
//计算二叉树结点的个数
int Size(Node *ptr)
{
	if (ptr == 0) return 0;
	return 1 + Size(ptr->lchild) + Size(ptr->rchild);
}
//计算二叉树叶子节点的个数
int LeafCount(Node *ptr)
{
	if (ptr == 0)return 0;
	if (ptr->lchild == 0 || ptr->rchild == 0)return 1;
	else return LeafCount(ptr->lchild) + LeafCount(ptr->rchild);
}
/***************************************************/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值