二叉树的遍历
用栈的步骤模拟一遍前中后序遍历,关键是在cur==NULL的时候return,接着返回栈的之前保存的地址接着往下执行,详情请参考王道
- 首先是二叉树节点的定义
/**
* 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) {}
* };
*/
- 前序遍历
前表示的是中节点在前面,一进入traversal递归就先把中节点压入vec,然后再遍历左节点,遍历到最后一个即最左下的子节点traversal(cur->left, vec);后就会cur==NULL然后return出来,此时的栈顶是traversal(cur->left, vec);接着往下执行遍历当前节点的右节点traversal(cur->right, vec),接着又会进来继续中左右的顺序,就是右子树节点的中左右顺序遍历。
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec)
{
if(cur==NULL)
{
return;
}
vec.push_back(cur->val); //中
traversal(cur->left, vec); //左
traversal(cur->right, vec); //右
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int>vec;
traversal(root,vec);
return vec;
}
};
- 中序遍历
左中右,在一开始一直进入traversal(cur->left, vec);递归循环,直到找到了最左下的子节点,然后cur==NULL后return出来返回到cur节点(本质上为最左下的子节点,然后cur->val压入vec)
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec)
{
if(cur==NULL)
{
return;
}
traversal(cur->left, vec);
vec.push_back(cur->val);
traversal(cur->right, vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> vec;
traversal(root, vec);
return vec;
}
};
- 后序遍历
左右中,后代表的是中节点在第三个,递归过程和上面一致
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec)
{
if(cur==NULL)
{
return;
}
traversal(cur->left, vec);
traversal(cur->right, vec);
vec.push_back(cur->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> vec;
traversal(root, vec);
return vec;
}
};
迭代遍历
前序遍历(中左右)
迭代遍历的前序遍历,卡哥后面说了,因为要访问的元素和要处理的元素顺序是一致的,都是中间节点。所以写起来比较容易,但是前序和后序就不一样了。
看动图是最直观的,就是中间节点先入栈接着出栈(顺便压入vector),出栈后判断中节点是否有左右孩子,并且先压入右孩子再压入左孩子(因为栈是后进先出的),最后加上安全判断,比如如果根节点是空的,还有用while来循环。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*>st;
vector<int>result;
if(root==NULL)
{
return result;
}
st.push(root);
while(!st.empty())
{
TreeNode* node = st.top();
result.push_back(node->val);
st.pop();
if(node->right)
{
st.push(node->right);
}
if(node->left)
{
st.push(node->left);
}
}
return result;
}
};
后序遍历
再来看后序遍历,先序遍历是中左右,后序遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*>st;
vector<int>result;
st.push(root);
if(root==NULL)
{
return result;
}
while(!st.empty())
{
TreeNode* node = st.top();
result.push_back(node->val);
st.pop();
if(node->left)
{
st.push(node->left);
}
if(node->right)
{
st.push(node->right);
}
}
reverse(result.begin(), result.end());
return result;
}
};
中序遍历
需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
流程:
- cur指针一直变left孩子压入栈,压到叶子节点为止;
- 当栈顶出栈的时候,把cur指针变为right孩子
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*>st;
vector<int>result;
TreeNode* cur = root;
while(cur!=NULL || !st.empty())
{
if(cur!=NULL)
{
st.push(cur);
cur = cur->left;
}else
{
cur = st.top();
st.pop();
result.push_back(cur->val);
cur = cur->right;
}
}
return result;
}
};
层序遍历
广度优先搜索的模板
- 用队列来模拟广度优先搜索的节点
- 先把root节点压入队列
- 用size变量来记录本层有多少个节点
- 每弹出一个节点,就往队列后面压入该节点左右孩子
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*>que;
vector<vector<int>>result;
if(root!=NULL)
{
que.push(root);
}
while(que.size()!=0)
{
int size = que.size();
vector<int>vec;
while(size--)
{
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if(node->left)
{
que.push(node->left);
}
if(node->right)
{
que.push(node->right);
}
}
result.push_back(vec);
}
return result;
}
};