你还没搞定二叉树遍历吗!(数据结构系列)

Posted 何唯编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你还没搞定二叉树遍历吗!(数据结构系列)相关的知识,希望对你有一定的参考价值。


鉴于有很多关注我的小伙伴是考研并且学生党,今天我整理了一些关于数据结构树当中关于遍历的知识。

标语:学习就是一个不断复习的过程,领悟便可创新,多数令人逐渐遗忘~

二叉树的相关概念总结下来就

  • 二叉树是数据结构当中的重中之重。

  • 二叉树是一个树形结构,该结构可以为空。

  • 每个节点最多有两棵子树,且有左右之分。


  作者所使用的语言是C++,其实是和C语言并没有什么太大的区别哦

  首先看看二叉树的样子

弱覆盖你还没搞定二叉树遍历吗!(数据结构系列)

  大家其实可以注意到,这个圈圈叫做节点,而除了根节点(最上面的节点)的 节点叫做叶子节点。

  二叉树的基础操作有很多,我们在这里简单介绍一下,创建节点的操作。想要创建节点,就必须要知道他的结构体类型,data代表数据,也就是圈圈里的值(可以为任何类型),LeftChild 指向左节点的指针RightChild 指向右节点的指针

  

typedef struct BinTreeNode{ DataType data; struct BinTreeNode* LeftChild; struct BinTreeNode* RightChild;}BinTreeNode;//创建节点

创建节点的详细代码:

void BinTreeCreat_1(BinTreeNode** t)//记住我们传的是树根的地址,这个**你应该知道了奥{ DataType item; scanf("%c", &item); if (item == '#') { *t = NULL; } else { *t = (BinTreeNode*)malloc(sizeof(BinTreeNode)); assert(*t != NULL); (*t)->data = item; BinTreeCreat_1(&((*t)->LeftChild)); BinTreeCreat_1(&((*t)->RightChild)); }}

细心的你可以看到scanf语句,这个就代表输入,这样我们就可以指挥树创建成什么样子啦!我们规定输入#代表空值,也就是上一个节点没有左叶子或者右叶子。


下面才到了我们今天讨论的众点:二叉树的遍历

二叉树的遍历分为四种:我将给出两种不同的方法进行描述(递归与非递归方法),下面所有的代码都已经leetcode测试过并且AC超过90%,好几种代码已经超过100%。

注意:递归与非递归的代码基本都是相同的,大家理解之后,记住一个,其他的记住就方便多了。

层次遍历:就是按照每层的顺序,依次从左到右输出

你还没搞定二叉树遍历吗!(数据结构系列)

二叉树的层次遍历/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */class Solution {public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>>vec;//定义一个二维数组 if(root == nullptr)return vec;//若树为空时,直接进行返回 queue<TreeNode*>q; //借用队列先入先出的性质 q.push(root);//先对根节点进行入栈 vector<int> v_fir;  while(!q.empty()) { int sz = q.size();//取队列头部的元素的个数,也就是二叉树中某层元素的个数 for(int i = 0;i < sz;i++) { TreeNode *temp = q.front();q.pop(); v_fir.push_back(temp->val); //先将二维数组每一行的值记录下来 if(temp->left) q.push(temp->left); if(temp->right)q.push(temp->right);//将某节点的左右子树记录下 } vec.push_back(v_fir);//将一维数组放入二维数组的每一行 v_fir.erase(v_fir.begin(),v_fir.end());//再将一维数组清空,继续进行插入操作 } return vec; }};

先序遍历:记住要点,根左右


你还没搞定二叉树遍历吗!(数据结构系列)

思路与解析:先将根节点的值放入,然后递归左右子树,到空节点为止

代码一:递归方法/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */class Solution {public: void dfs(TreeNode*root,vector<int>&v){        if(root == nullptr)return ;//终止条件 v.push_back(root->val);// dfs(root -> left,v); dfs(root->right,v);//递归左右子树 } vector<int> preorderTraversal(TreeNode* root) { vector<int>v; dfs(root,v); return v; }};

代码二:非递归

思路与解析:

使用栈来模拟递归的操作:循环条件:节点不为NULL,且栈不为空。

  • 若当前节点不为空,则访问该节点,将该节点入栈,不断递归左子树。

  • 若左子树为空,则去寻找他的右子树,不断进行上述过程,直到所有节点全部遍历到(栈为空时)。


/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */class Solution {public: vector<int> preorderTraversal(TreeNode* root) { vector<int>v; stack<TreeNode*>s; while(!s.empty()||root) {      while(root)//若当前节点不为空 {         v.push_back(root->val);//将当前节点的值入数组         s.push(root);//将当前节点入栈         root = root->left;//递归左子树 } TreeNode*temp = s.top(); s.pop();        root = temp->right; //若左子树为空,便将节点指向右子树 } return v; }};


中序遍历:记住要点:左根右    

代码一:递归

思路与解析:先递归左子树,递归完左子树进行将值放入数组,递归右子树

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */class Solution {public: void dfs(TreeNode*root,vector<int> &v){ if(root == nullptr)return ; if(root->left) dfs(root->left,v); v.push_back(root->val); if(root->right) dfs(root->right,v); } vector<int> inorderTraversal(TreeNode* root) { vector<int>v; dfs(root,v); return v; }};

代码二:非递归

思路与解析:和前序遍历类似,只不过访问节点从第一次进栈时变成出栈时访问节点。

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */class Solution {public: vector<int> inorderTraversal(TreeNode* root) { vector<int>v; stack<TreeNode*>s; while(!s.empty()||root) { while(root) { root = root->left; } s.push(root); TreeNode*temp = s.top(); s.pop(); v.push_back(temp->val); root = temp->right;
} return v; }};

后序递归:记住要点:左右根

思路与解析:递归左右子树,然后记录其值


/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */class Solution {public: void dfs(TreeNode* root,vector<int>&v){ if(root == nullptr)return ; dfs(root->left,v); dfs(root->right,v); v.push_back(root->val); } vector<int> postorderTraversal(TreeNode* root) { vector<int>v; dfs(root,v); return v; }};

代码二:非递归

思路与解析:后序遍历的难点在于第一次将根节点出栈后,并不能直接访问,必须访问其右子树后才可以。非递归后序遍历有很多种实现方式,我个人比较喜欢,使用一个状态栈来存储栈中元素的状态,状态为0暂时不可访问,状态为1可以访问。

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */class Solution {public: vector<int> postorderTraversal(TreeNode* root) { vector<int>v; stack<TreeNode*>s1; stack<int>s2; while(root||!s1.empty()) { while(root) { s1.push(root); s2.push(0); root = root->left; } TreeNode*temp = s1.top(); cout<<temp->val<<endl; int flag = s2.top(); if(flag == 0) { root = temp->right; s2.pop(); s2.push(1); } else if(flag == 1) { v.push_back(temp->val); s1.pop();s2.pop(); } } return v; }};

                                                                                     

以上是关于你还没搞定二叉树遍历吗!(数据结构系列)的主要内容,如果未能解决你的问题,请参考以下文章

一文彻底搞定二叉树的前序中序后序遍历(图解递归非递归)

二叉树遍历算法

一文搞定二叉树---由二叉树到贪心算法

一文搞定二叉树遍历

写写代码系列028:二叉树的深度优先遍历

Python数据结构系列☀️《树与二叉树-基础知识》——知识点讲解+代码实现☀️