二叉树详解

5 篇文章 0 订阅

目录

树的介绍

树的定义

树的性质:

二叉树

二叉树的定义

 二叉树与度为2的树的区别

二叉树的性质

二叉树的种类

二叉树的存储方式

顺序存储

链式存储

静态存储

二叉树的遍历方式

前序遍历

中序遍历

后续遍历

层次遍历

Z字型遍历

二叉树的创建方式

根据前序和中序遍历创建

根据后序和中序遍历创建

二叉树的节点个数和深度求解


树的介绍

树的定义

树是由n(n>=0)个节点组成的有限集合。如果n=0,称为空树;如果n>0,则:

有一个特定的称之为根(root)的节点,它只有直接后继,但没有直接前驱;

除根以外的其它节点划分为m(m>=0)个互不相交的有限集合,每个集合又是一棵树,并且称之为根的子树,没棵子树的根节点有且只有一个直接前驱,但可以有0个或多个直接后继

树的性质:

  • 节点的度:一个节点含有的子树的个数称为该节点的度;
  • 树的度:一棵树中,最大的节点的度称为树的度(这里不一定是根节点,如:A的度为2,C的度为3,那么这棵树的度就位3)
  • 叶节点或终端节点的度为0
  • 非终端节点/分支节点是度不为0的节点
  • 父亲节点:若一个接地那还有子节点,则这个节点称为其子节点的父节点
  • 孩子节点:一个节点含有的子树的根节点称为该节点的子节点
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点
  • 节点的层次:从根开始定义起,根为第一层,根的子节点为第2层,以此类推
  • 深度:对于任意节点n,n的深度为从根到n的唯一路径长,跟的深度为0
  • 高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有的树叶的高度为0
  • 堂兄弟节点:父节点在同一层的节点互称为堂兄弟
  • 节点的祖先:从根到该节点所经分支的所有节点
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙
  • 森林:由m(m>=0)棵互不相交的树称为森林
  • 树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树。反之是有序树

二叉树

二叉树的定义

所谓二叉树,即一棵只有两个分叉的树,每个节点最多有2个子树。他有五种形态:

 二叉树与度为2的树的区别

  1. 二叉树的度不一定为2,比如只存在左子树或者右子树
  2. 二叉树的树可以为空,但是度为2的树至少有一个节点含有两个子树

二叉树的性质

  1. 若二叉树的层次从0开始,则在二叉树的第i层最多有2^i个节点。(i>=0)
  2. 高度为k的二叉树最多有2^(k+1)-1个节点(k>=0)
  3. 对于任何一棵二叉树,如果其叶节点个数为n0,度为2的非节点个数为n2,则有n0=n2+1
  4. 包含n个节点的二叉树的高度至少为log2(n+1)

二叉树的种类

满二叉树:每一层的节点数均达到最大值

完全二叉树:除最后一层之外,其它层均达到了最大值,并且最后一层是从右向左依次进行缺省(满二叉树是一种特殊的完全二叉树)

注意:左右是满二叉树,但整体不一定是满二叉树,同样,左右子树是完全二叉树,但整体不一定是完全二叉树,如图:

二叉树的存储方式

顺序存储

顺序存储就是用数组来存储,如图:

 由图看出:当二叉树不为完全二叉树时,容易造成空间浪费

链式存储

链式存储就是通过指针把散落的地址穿起来,如图:

静态存储

二叉树的遍历方式

节点定义:

typedef int ElemType;

typedef struct BtNode
{
	ElemType data;
	struct BtNode* leftchild;
	struct BtNode* rightchild;
}BtNode,*BinaryTree;

前序遍历

前序遍历就是先访问根节点,再访问左子树,最后再访问右子树

代码实现:

//递归
void PreOrder(BtNode* p)
{
	if (p != NULL)
	{
		cout << p->data << " ";
		PreOrder(p->leftchild);
		PreOrder(p->rightchild);
	}
}
//非递归遍历
void NicePreOrder(BtNode* ptr)
{
	if (ptr == NULL)return;
	std::stack<BtNode*>st;
	st.push(ptr);
	while (!st.empty())
	{
		ptr = st.top(); st.pop();
		cout << ptr->data;
		if (ptr->rightchild != NULL)
		{
			st.push(ptr->rightchild);
		}
		if (ptr->leftchild != NULL)
		{
			st.push(ptr->leftchild);
		}
	}
	cout << endl;
}

中序遍历

中序遍历就是先访问左子树,再访问根节点,最后再访问右子树

代码实现:

//递归遍历
void InOrder(BtNode* p)
{
	if (p!= NULL)
	{
		InOrder(p->leftchild);
		cout << p->data << " ";
		InOrder(p->rightchild);
	}
}

//非递归遍历
void NiceInOrder(BtNode* ptr)
{
	if (ptr == NULL)return;
	std::stack<BtNode*>st;
	while (ptr != NULL || !st.empty())
	{ 
		while (ptr != NULL)
		{
			st.push(ptr);
			ptr = ptr->leftchild;
		}
		ptr = st.top(); st.pop();
		cout << ptr->data;
		ptr = ptr->rightchild;
	}
}

后续遍历

后序遍历就是先访问左子树,再访问右子树,最后再访问根节点

 

代码实现:

//递归遍历
void PastOrder(BtNode* p)
{
	if (p != NULL)
	{
		PastOrder(p->leftchild);
		PastOrder(p->rightchild);
		cout << p->data << " ";
	}
}

//非递归遍历
void NicePastOrder(BtNode* ptr)
{
	if (ptr == NULL)return;
	std::stack<BtNode*>st;
	BtNode* tag = NULL;
	while (ptr != NULL || !st.empty())
	{
		while (ptr!= NULL)
		{
			st.push(ptr);
			ptr = ptr->leftchild;
		}
		ptr = st.top(); st.pop();
		if (ptr->rightchild == NULL || ptr->rightchild == tag)
		{
			cout << ptr->data;
			tag = ptr;
			ptr = NULL;
		}
		else
		{			
			st.push(ptr);
			ptr = ptr->rightchild;
		}
	}
}

层次遍历

代码实现:


void LevelOrder(BtNode* ptr)
{
	if (ptr == NULL)return;
	queue<BtNode*>qu;
	qu.push(ptr);
	while (!qu.empty())
	{
		ptr = qu.front(); qu.pop();
		cout << ptr->data;
		if (ptr->leftchild != NULL)qu.push(ptr->leftchild);
		if (ptr->rightchild != NULL)qu.push(ptr->rightchild);
	}
}

Z字型遍历

void LevelZOrder(BtNode* ptr)
{
	if (ptr == NULL)return;
	stack<BtNode*>st1;
	stack<BtNode*>st2;
	st1.push(ptr);
	while (!st1.empty()||!st2.empty())
	{
		while(!st1.empty())
		{ 
			ptr = st1.top(); st1.pop();
			cout << ptr->data;
			if (ptr->leftchild != NULL)st2.push(ptr->leftchild);
			if (ptr->rightchild != NULL)st2.push(ptr->rightchild);
		}
		while (!st2.empty())
		{
			ptr = st2.top(); st2.pop();
			cout << ptr->data;
			if (ptr->leftchild != NULL)st1.push(ptr->leftchild);
			if (ptr->rightchild != NULL)st1.push(ptr->rightchild);
		}
	}
	cout << endl;
}

二叉树的创建方式

根据前序和中序遍历创建

描述:对于前序遍历,我们如果拿第一个节点p去中序遍历顺序中查找它的下标,那么中序遍历顺序对应下标的左半部分就是p的左子树,右半部分就是p的右子树。同时我们可以根据这个下标在前序遍历顺序中划分出左子树和右子树的前序遍历顺序。循环上述步骤,我们就可以构建出这个二叉树。此方法的难点就是如何确定下一次递归时的前序和后序的指针位置以及规模(也就是子树的节点个数)

代码实现:

int FindIs(const char* is, int n, char ch)
{
	int pos = -1;
	for (int i = 0; i < n; ++i)
	{
		if (is[i] == ch)
		{
			pos = i;
			break;
		}
	}
	return pos;
}

BtNode* CreatePI(const char* ps, const char* is, int n)
{
	BtNode* s = NULL;
	if (n >= 1)
	{
		s = Buynode();
		s->data = ps[0];
		int pos = FindIs(is, n, ps[0]);
		if (pos == -1)exit(1);
		s->leftchild = CreatePI(ps + 1, is, pos);
		s->rightchild = CreatePI(ps + pos + 1, is + pos + 1, n - pos - 1);
	}
    return s;
}

BtNode* CreateBinartTreePI(const char* ps, const char* is, int n)
{
	if (ps == NULL || is == NULL || n <= 0)return NULL;
	else return CreatePI(ps, is, n);
}

int main()
{
	char ps[] = { "ABCDEFGH" };
	char is[] = { "CBEDFAGH" };
	char ls[] = { "CEFDBHGA" };
	int n = strlen(ps);
	BinaryTree root = NULL;
	root = CreateBinartTreePI(ps, is, n);
}

根据后序和中序遍历创建

思想和前面相似

BtNode* CreateBinartTreePI(const char* ps, const char* is, int n)
{
	if (ps == NULL || is == NULL || n <= 0)return NULL;
	else return CreatePI(ps, is, n);
}


BtNode* CreateIL(const char* is, const char* ls, int n)
{
	BtNode* s = NULL;
	if (n > 0)
	{
		s = Buynode();
		s->data = ls[n - 1];
		int pos= FindIs(is, n, ls[n-1]);
		if (pos == -1)exit(1);
		s->leftchild = CreatePI(is, ls, pos);
		s->rightchild = CreatePI(is+pos+1,ls+pos,n-pos-1);
	}
	return s;
}

BtNode* CreateBinartTreeII(const char* is, const char* ls, int n)
{
	if (is == NULL || ls == NULL || n <= 0)return NULL;
	else return CreatePI(is, ls, n);
}

二叉树的节点个数和深度求解

int Count(BtNode* ptr)
{
	if (ptr == NULL)return 0;
	else 
		return Count(ptr->leftchild) + Count(ptr->rightchild) + 1;
}
int Depth(BtNode* ptr)
{
	if (ptr == NULL)return 0;
	else
		return std::max(Depth(ptr->leftchild) , Depth(ptr->rightchild)) + 1;
}

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值