当前位置:   article > 正文

链式二叉树详解

链式二叉树

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

目录

一、什么是链式二叉树 

二、链式二叉树遍历

1.先序遍历

2.中序遍历

 2.后续序遍历

三、链式二叉树结点个数

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

 五、链式二叉树的高度

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

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

八、总结


一、什么是链式二叉树 

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

树的结构无外乎以下五种

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

  1. typedef int BTDataType;
  2. typedef struct BinaryTreeNode
  3. {
  4. BTDataType _data;
  5. BinaryTreeNode* _left;
  6. BinaryTreeNode* _right;
  7. BinaryTreeNode(BTDataType data = 0,
  8. BinaryTreeNode* left = nullptr,
  9. BinaryTreeNode* right = nullptr)
  10. :_data(data)
  11. ,_left(left)
  12. ,_right(right)
  13. {}
  14. }BTNode;

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

二、链式二叉树遍历

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

1.先序遍历

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

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

  1. BTNode* root1 = new BTNode(1);
  2. BTNode* root2 = new BTNode(2);
  3. BTNode* root3 = new BTNode(3);
  4. BTNode* root4 = new BTNode(4);
  5. BTNode* root5 = new BTNode(5);
  6. BTNode* root6 = new BTNode(6);
  7. root1->_left = root2;
  8. root1->_right = root4;
  9. root2->_left = root3;
  10. root4->_left = root5;
  11. root4->_right = root6;

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

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

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

  1. void BinaryTreePrevOrder(BTNode* root)
  2. {
  3. if (root == nullptr)
  4. {
  5. cout << "NULL ";
  6. return;
  7. }
  8. cout << root->_data << " ";
  9. BinaryTreePrevOrder(root->_left);
  10. BinaryTreePrevOrder(root->_right);
  11. }

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

下面附上执行结果。

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

2.中序遍历

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

  1. void BinaryTreeInOrder(BTNode* root)
  2. {
  3. if (root == nullptr)
  4. {
  5. cout << "NULL ";
  6. return;
  7. }
  8. BinaryTreePrevOrder(root->_left);
  9. cout << root->_data << " ";
  10. BinaryTreePrevOrder(root->_right);
  11. }

 2.后续序遍历

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

  1. void BinaryTreePostOrder(BTNode* root)
  2. {
  3. if (root == nullptr)
  4. {
  5. cout << "NULL ";
  6. return;
  7. }
  8. BinaryTreePrevOrder(root->_left);
  9. BinaryTreePrevOrder(root->_right);
  10. cout << root->_data << " ";
  11. }

三、链式二叉树结点个数

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

  1. int BinaryTreeSize(BTNode* root)
  2. {
  3. if (root == nullptr)
  4. return 0;
  5. return BinaryTreeSize(root->_left) + 1 + BinaryTreeSize(root->_right);
  6. }

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

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

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

  1. int BinaryTreeLeafSize(BTNode* root)
  2. {
  3. if (root == nullptr)
  4. return 0;
  5. if (root->_left == nullptr && root->_right == nullptr)
  6. return 1;
  7. return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
  8. }

 五、链式二叉树的高度

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

  1. int BinaryTreeHeight(BTNode* root)
  2. {
  3. if (root == nullptr)
  4. return 0;
  5. int leftHeight = BinaryTreeHeight(root->_left);
  6. int rightHeight = BinaryTreeHeight(root->_right);
  7. return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
  8. }

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

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

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

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

  1. int BinaryTreeLevelKSize(BTNode* root, int k)
  2. {
  3. assert(k > 0);
  4. if (root == nullptr)
  5. return 0;
  6. if (k == 1)
  7. return 1;
  8. return BinaryTreeLevelKSize(root->_left, k - 1) +
  9. BinaryTreeLevelKSize(root->_right, k - 1);
  10. }

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

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

  1. BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
  2. {
  3. if (root == nullptr)
  4. return 0;
  5. if (root->_data == x)
  6. {
  7. return root;
  8. }
  9. BTNode* cur = BinaryTreeFind(root->_left, x);
  10. if (cur == nullptr)
  11. cur = BinaryTreeFind(root->_right, x);
  12. return cur;
  13. }

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

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

八、总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/674136
推荐阅读
相关标签
  

闽ICP备14008679号