赞
踩
前言:链式二叉树的操作在我看来是一个很难掌握的东西,有些题真的是做不来,甚至是一点思路都没有,真的让人感到打击,这还是归咎于自己知识掌握不牢固,递归处理得不好,而树的结构天然地适合递归的处理方式,不用递归要想处理就更难了,因此学好二叉树的简单递归,在面对更难的题的时候,才能游刃有余,让我们一起学习吧!!!
目录
链式二叉树是一种常见的数据结构,它由多个节点组成,每个节点可以有最多两个子节点。
树的结构无外乎以下五种
我们给他的两个子节点分别取名为左节点和右节点,再加上节点本身所存下的值,因此我们可以写出如下的结构体
- 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):遍历是指按照一定的顺序访问树中的所有节点。常见的遍历方式包括前序遍历、中序遍历和后序遍历。
先序遍历基本思想是从树的根节点开始,先访问当前节点,然后依次遍历左子节点和右子节点。以下是详细的先序遍历过程以及一个示例:
先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应该会更容易理解一些。
下面附上执行结果。
先序遍历我们掌握后,中序遍历和后序遍历也是小菜一碟 ,甚至代码都不需要修改,他们唯一的区别就是打印的位置。
中序遍历便是将打印放在左子树遍历后面
- void BinaryTreeInOrder(BTNode* root)
- {
- if (root == nullptr)
- {
- cout << "NULL ";
- return;
- }
- BinaryTreePrevOrder(root->_left);
- cout << root->_data << " ";
- BinaryTreePrevOrder(root->_right);
- }
后续遍历便是将打印放在左子树和右子树遍历后面
- 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);
- 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。
- 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,红黑树等等。大家一起加油吧
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。