这里三题是一起讲得,并且三题各有三种方法
方法1 递归遍历(必须掌握)
二叉树的三种递归遍历掌握其规律后,其实很简单
题目链接/文章讲解/视频讲解:代码随想录
方法2 迭代遍历(基础不好的录友,迭代法可以放过)
题目链接/文章讲解/视频讲解:代码随想录
方法3 统一迭代(基础不好的录友,迭代法可以放过)
这是统一迭代法的写法, 如果学有余力,可以掌握一下
题目链接/文章讲解:代码随想录
理论基础
需要了解 二叉树的种类,存储方式,遍历方式 以及二叉树的定义
文章讲解:代码随想录
144. 二叉树前序遍历
1.代码展现
//144.二叉树的前序遍历
// 递归法
//step1 确认参数,可以边写边确定
void preTravelsal(TreeNode* root, vector& vec) {
//step2 确认终止条件
if (root == nullptr) {
return;
}
//step3 确定单层递归逻辑,前序遍历为中左右
vec.push_back(root->val);
preTravelsal(root->left, vec);
preTravelsal(root->right, vec);
}
vector preorderTraversalA(TreeNode* root) {
vector vec;
preTravelsal(root, vec);
return vec;
}
//迭代法
vector preorderTraversalB(TreeNode* root) {
vector vec;
stack stNode;
if (root == nullptr) {
return vec;
}
//step1 给栈加入根节点
stNode.push(root);
//step2 进入while循环,展开节点
while (!stNode.empty()) {
//先中,添加后剔除
TreeNode* node = stNode.top();
vec.push_back(node->val);
stNode.pop();
//再左最后再右,但是由于栈是先进后出,
//因此先加入右节点,后加入左节点
if (node->right != nullptr) {
stNode.push(node->right);
}
if (node->left != nullptr) {
stNode.push(node->left);
}
}
return vec;
}
//统一迭代法
vector preorderTraversalC(TreeNode* root) {
vector vec;
stack stNode;
if (root != nullptr) {
stNode.push(root);
}
while (!stNode.empty()) {
//step1 node不为空时
TreeNode* node = stNode.top();
if (node != nullptr) {
stNode.pop();
//前序遍历中左右
//压栈时右左中
if (node->right != nullptr) {
stNode.push(node->right);
}
if (node->left != nullptr) {
stNode.push(node->left);
}
//中间节点做标记
stNode.push(node);
stNode.push(nullptr);
}
//step2 node为空时
else {
stNode.pop();
vec.push_back(stNode.top()->val);
stNode.pop();
}
}
return vec;
}
2.本题小节
递归法:递归法的核心有三个。1.首先要明确参数,可以边写边添加,本题参数是储存结果的容器以及当前节点;2.明确中止条件,这是结束递归的条件,本题是当节点指向为空时中止;3.明确单层递归逻辑,这里是中左右依次传入结果。递归实际上也不难理解,他为什么会返回,为什么会这么神奇?实际上追究到底还是程序的执行顺序以及返回条件使得它看起来这么神奇,自己举个例子,一层一层的扒开,就能发现,实际上就是程序嵌套以及程序并列,嵌套结束返回上一个程序,并列结束开始下一个程序等等等(这个说法应该就我自己理解吧,哈哈哈哈哈哈)。中序遍历和后序遍历只是递归逻辑不同,换换代码就好饿
迭代法:前序的迭代法相对于后序和中序好理解一些,这里主要用到了栈来储存每次的节点,初始是给栈添加根节点,然后进入while循环,条件为栈非空,由于是前序遍历,首先取出栈头,再pop掉,将其中的val传入到vec中,再将left,right压入栈中,这里先压入right,再压入left,因为right要先出来(条件为非空)。然后每次循环都这样,这样就能保证按照前序来取出所有的结果。后序的话这里使用的比较巧妙,后序是左右中,前序是中左右,压栈的顺序为中右左,把压栈的顺序改为中左右,此时去除的vec为中右左,然后将该结果反转一下,则为左右中,即后序。中序迭代法这里就不是很明了了,因为这里的节点遍历和节点处理顺序不一样,因此需要新建一个TreeNode指针来遍历,使用指针一直寻找左侧最底部的节点,并将沿途找到的放入到栈中,如果左侧节点不存在的话,就取栈头的节点,并pop掉,将其中的val放入vec中(左或者认为中,也可能为右,)并将节点指针指向right,开始下一次迭代,这里实际上就是按照左中右的顺序来的,我只能说很神奇,自己画一画就知道了。
统一迭代法:这个我就不展开说了,自己也是通过例子觉得程序可行,然后背着写了下来,主要是node节点不为空是一种情况,此时按照前中后序的遍历的相反顺序压栈(left,right为空时不压入),注意中节点压栈后需要添加nullptr作为标记(这是迭代法统一的妙点),当节点为空时,pop两次,取第二次的val放入vec中即可。自己画一画,配合程序看,觉得还是很神奇的。
还是递归舒服!!!
145.二叉树后序遍历
1.代码展现
//145.二叉树的后序遍历
//step1 确认参数,可以边写边确定
void postTravelsal(TreeNode* root, vector& vec) {
//step2 确认终止条件
if (root == nullptr) {
return;
}
//step3 确定单层递归逻辑,前序遍历为左右中
postTravelsal(root->left, vec);
postTravelsal(root->right, vec);
vec.push_back(root->val);
}
vector postorderTraversalA(TreeNode* root) {
vector vec;
postTravelsal(root, vec);
return vec;
}
//迭代法
vector postorderTraversalB(TreeNode* root) {
vector vec;
stack stNode;
if (root == nullptr) {
return vec;
}
//step1 给栈加入根节点
stNode.push(root);
//step2 进入while循环,展开节点
while (!stNode.empty()) {
//先中,添加后剔除
TreeNode* node = stNode.top();
vec.push_back(node->val);
stNode.pop();
//再右最后再左,但是由于栈是先进后出,
//因此先加入左节点,后加入右节点
if (node->left != nullptr) {
stNode.push(node->left);
}
if (node->right != nullptr) {
stNode.push(node->right);
}
}
//step3 此时是中右左,反转一下数组,左中右即后续遍历
reverse(vec.begin(), vec.end());
return vec;
}
//统一迭代法
vector postorderTraversalC(TreeNode* root) {
vector vec;
stack stNode;
if (root != nullptr) {
stNode.push(root);
}
while (!stNode.empty()) {
//step1 node不为空时
TreeNode* node = stNode.top();
if (node != nullptr) {
stNode.pop();
//前序遍历左右中
//压栈时中右左
//中间节点做标记
stNode.push(node);
stNode.push(nullptr);
if (node->right != nullptr) {
stNode.push(node->right);
}
if (node->left != nullptr) {
stNode.push(node->left);
}
}
//step2 node为空时
else {
stNode.pop();
vec.push_back(stNode.top()->val);
stNode.pop();
}
}
return vec;
}
94.二叉树中序遍历
1.代码展现
//94.二叉树的中序遍历
//step1 确认参数,可以边写边确定
void inTravelsal(TreeNode* root, vector& vec) {
//step2 确认终止条件
if (root == nullptr) {
return;
}
//step3 确定单层递归逻辑,前序遍历为左右中
inTravelsal(root->left, vec);
vec.push_back(root->val);
inTravelsal(root->right, vec);
}
vector inorderTraversalA(TreeNode* root) {
vector vec;
inTravelsal(root, vec);
return vec;
}
//迭代法
vector inorderTraversalB(TreeNode* root) {
vector vec;
stack stNode;
TreeNode* node = root;
while (node != nullptr || !stNode.empty()) {
//step1 指针一直寻找左子树到最底层
if (node != nullptr) {
//记录下来
stNode.push(node);
node = node->left;
}
else {
//step2 找到了最底层的下面一个
TreeNode* temNode = stNode.top();
stNode.pop();
vec.push_back(temNode->val);
node = temNode->right;
}
}
return vec;
}
//统一迭代法
vector inorderTraversalC(TreeNode* root) {
vector vec;
stack stNode;
if (root != nullptr) {
stNode.push(root);
}
while (!stNode.empty()) {
//step1 node不为空时
TreeNode* node = stNode.top();
if (node != nullptr) {
stNode.pop();
//前序遍历左中右
//压栈时右中左
if (node->right != nullptr) {
stNode.push(node->right);
}
//中间节点做标记
stNode.push(node);
stNode.push(nullptr);
if (node->left != nullptr) {
stNode.push(node->left);
}
}
//step2 node为空时
else {
stNode.pop();
vec.push_back(stNode.top()->val);
stNode.pop();
}
}
return vec;
}