链式二叉树详解

前言:链式二叉树的操作在我看来是一个很难掌握的东西,有些题真的是做不来,甚至是一点思路都没有,真的让人感到打击,这还是归咎于自己知识掌握不牢固,递归处理得不好,而树的结构天然地适合递归的处理方式,不用递归要想处理就更难了,因此学好二叉树的简单递归,在面对更难的题的时候,才能游刃有余,让我们一起学习吧!!!

目录

一、什么是链式二叉树 

二、链式二叉树遍历

1.先序遍历

2.中序遍历

 2.后续序遍历

三、链式二叉树结点个数

四、链式二叉树叶子结点个数

 五、链式二叉树的高度

  六、链式二叉树第k层节点个数

   七、链式二叉树查找值为x的结点

八、总结


一、什么是链式二叉树 

链式二叉树是一种常见的数据结构,它由多个节点组成,每个节点可以有最多两个子节点。 

树的结构无外乎以下五种

 我们给他的两个子节点分别取名为左节点和右节点,再加上节点本身所存下的值,因此我们可以写出如下的结构体

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType _data;
	BinaryTreeNode* _left;
	BinaryTreeNode* _right;
	BinaryTreeNode(BTDataType data = 0, 
                   BinaryTreeNode* left = nullptr, 
                   BinaryTreeNode* right = nullptr)
		:_data(data)
		,_left(left)
		,_right(right)
	{}
}BTNode;

这里我们使用了C++的构造函数,并给上了默认的缺省值,想创造一个节点,new一个就好了。我们选择的讲法和其他的数据结构不一样,比如顺序表和链表亦或是栈和队列,我们都是讲解了他的增删改查,但是一个普通的链式二叉树不一样,他存在的本身其实意义并不大(因为结构的复杂且无序),也不需要增删改操作,我们要做的是初步了解他的结构,并熟悉简单的二叉树递归即可。

二、链式二叉树遍历

首先我们来看看二叉树的遍历(Traversal):遍历是指按照一定的顺序访问树中的所有节点。常见的遍历方式包括前序遍历、中序遍历和后序遍历。

1.先序遍历

先序遍历基本思想是从树的根节点开始,先访问当前节点,然后依次遍历左子节点和右子节点。以下是详细的先序遍历过程以及一个示例:

先new一些节点,并把他们连接起来

	BTNode* root1 = new BTNode(1);
	BTNode* root2 = new BTNode(2);
	BTNode* root3 = new BTNode(3);
	BTNode* root4 = new BTNode(4);
	BTNode* root5 = new BTNode(5);
	BTNode* root6 = new BTNode(6);
	root1->_left = root2;
	root1->_right = root4;
	root2->_left = root3;
	root4->_left = root5;
	root4->_right = root6;

我们的构造节点以及链接关系如下图所示 

我们的访问顺序从一开始,一步一步走,先走左,在左右,如果打印空节点,我们便走出了下面的轨迹

 而使用代码去实现也是非常简单的

void BinaryTreePrevOrder(BTNode* root)
{
	if (root == nullptr)
	{
		cout << "NULL ";
		return;
	}
	cout << root->_data << " ";
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}

root 不为空便打印这个值存放的数据,一般情况下是可以不打印空的,但是我们这里打印了NULL应该会更容易理解一些。

下面附上执行结果。

先序遍历我们掌握后,中序遍历和后序遍历也是小菜一碟 ,甚至代码都不需要修改,他们唯一的区别就是打印的位置。

2.中序遍历

中序遍历便是将打印放在左子树遍历后面

void BinaryTreeInOrder(BTNode* root)
{
	if (root == nullptr)
	{
		cout << "NULL ";
		return;
	}
	BinaryTreePrevOrder(root->_left);
	cout << root->_data << " ";
	BinaryTreePrevOrder(root->_right);
}

 2.后续序遍历

后续遍历便是将打印放在左子树和右子树遍历后面

void BinaryTreePostOrder(BTNode* root)
{
	if (root == nullptr)
	{
		cout << "NULL ";
		return;
	}
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
	cout << root->_data << " ";
}

三、链式二叉树结点个数

我们求二叉树结点的个数,依旧是使用遍历。首先我们要检查当前节点是否为空,如果为空,则返回 0,表示没有节点。如果当前节点不为空,则递归地计算左子树和右子树的节点个数,然后将它们相加,并加上当前节点自身,得到整棵树的节点个数。

int BinaryTreeSize(BTNode* root)
{
	if (root == nullptr)
		return 0;
	return BinaryTreeSize(root->_left) + 1 + BinaryTreeSize(root->_right);
}

这里使用的思想和遍历非常像,区别就是遍历是打印,找结点的个数是+1而已。

四、链式二叉树叶子结点个数

这个跟上面的求二叉树结点个数也很像,区别在于如果root节点左子树和右子树都为空,就返回1,再递归一下,这是归结于链式二叉树的特性,几乎每一题都需要采用递归思想,如果能深入掌握递归思想,二叉树的题也就迎刃而解了。

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == nullptr)
		return 0;
	if (root->_left == nullptr && root->_right == nullptr)
		return 1;
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

 五、链式二叉树的高度

我们定义了左边的高和右边的高,谁高谁就+1。

int BinaryTreeHeight(BTNode* root)
{
	if (root == nullptr)
		return 0;

	int leftHeight = BinaryTreeHeight(root->_left);
	int rightHeight = BinaryTreeHeight(root->_right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

我认为这里是有点不好理解的,我们遍历下去,遇到空返回0,于是结点3 的leftHeight和rightHeight都为0,然后会执行   return代码:   0>0  ? 0+1:0+1;   很明显结果是后面的语句,也就是rightHeight+1; 注意这里虽然是rightHeight+1,但是返回到上面接受它的是leftHeight,这里会比较容易混淆。

返回代码切记不要像下面这样写,虽然是正确的,但是它的时间复杂度会成指数倍上涨,他所递归的次数远远不止一倍而已。

	return BinaryTreeHeight(root->_left) > BinaryTreeHeight(root->_right) ? 
		   BinaryTreeHeight(root->_left) : BinaryTreeHeight(root->_right);

  六、链式二叉树第k层节点个数

int BinaryTreeLevelKSize(BTNode* root, int k)
{
    assert(k > 0);
	if (root == nullptr)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->_left, k - 1) + 
           BinaryTreeLevelKSize(root->_right, k - 1);
}

 这里的k就是代表了层数,每一次递归k就要减去1,当k==1的时候就代表了这一层,于是我们返回1。

   七、链式二叉树查找值为x的结点

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == nullptr)
		return 0;
	if (root->_data == x)
	{
		return root;
	}
	BTNode* cur = BinaryTreeFind(root->_left, x);
	if (cur == nullptr)
		cur = BinaryTreeFind(root->_right, x);
	return cur;
}

这里的返回值是BTNode*类型的,需要一个变量去接受他,不然我们找到后,递归回来的值会被扔掉后继续递归(因为你递归的层次很深,返回并不会直接结束整个函数,而是结束递归的这一个函数),得到的值会永远为空。

我们找到了值为x的结点,便会return回来,并用cur去接受它,此时cur不为空,便又return了,后续便会一直return知道结束本函数。

八、总结

二叉树还有一些操作,但上面这一些是较为重要的,学好了普通的链式二叉树,才能让我们更为轻松的学习之后的AVLTree,红黑树等等。大家一起加油吧

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值