1.二叉树的遍历方式:
- 深度优先遍历: 先向深处走,遇到叶子结点就返回
- 前序遍历: 根左右
- 中序遍历: 左根右
- 后序遍历: 左右根
- 广度优先遍历:一层一层的遍历,每次遍历全部子节点
- 层次遍历
- 层次遍历
2.二叉树的递归遍历
递归算法的三要素:
- 递归函数的参数和返回值
- 终止条件
- 递归的内容
/**
* 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:
// res 需要是引用类型
void preorder(TreeNode * root,vector<int>& res){
if(!root) return;
res.push_back(root->val); //访问根节点
preorder(root->left,res); //访问左孩子
preorder(root->right,res); //访问右孩子
}
void inorder(TreeNode * root,vector<int>& res){
if(!root) return;
inorder(root->left,res); //访问左孩子
res.push_back(root->val); //访问根节点
inorder(root->right,res); //访问右孩子
}
void postorder(TreeNode * root,vector<int>& res){
if(!root) return;
postorder(root->left,res); //访问左孩子
postorder(root->right,res); //访问右孩子
res.push_back(root->val); //访问根节点
}
};
3.二叉树的迭代遍历
递归都是通过栈实现的。所以可以借助栈实现三种遍历。
前序遍历
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> st;
st.push(root); //根节点进栈
while(!st.empty()){ //栈不空,还有节点未遍历
TreeNode *tmp=st.top();
res.push_back(tmp->val); //访问根节点
st.pop();
if(tmp){
if(tmp->right) st.push(tmp->right); //右孩子非空就先进,因为是栈所以会后访问
if(tmp->left) st.push(tmp->left);
}
}
return res;
}
中序遍历没办法像前序遍历一样,中序遍历是找到最左下角的结点开始遍历,然后返回转到其右孩子上重复此过程。
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> st;
TreeNode * tmp=root;
while(tmp||!st.empty()){
if(tmp){
st.push(tmp);
tmp=tmp->left; //一直向左走,走到最左下角
}else{
tmp=st.top(); //访问栈顶节点
res.push_back(tmp->val);
st.pop();
tmp=tmp->right;
}
}
return res;
}
4.统一迭代遍历
三种遍历中每个节点都经过两次,其中一次被访问。所以统一非递归遍历的模板的思路就是,第一次经过根节点的时候都不访问,按本应访问(根左右)顺序的逆序将节点进栈,使用nullptr做标记此次经过,下次再次经过就可以访问。
中序遍历
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()){
TreeNode * tmp=st.top();
if(tmp){ //结点未做标记,第一次经过这个中间结点
st.pop(); //第一次遍历根节点并不访问
if(tmp->right) st.push(tmp->right); //先弹入右孩子
st.push(tmp);
st.push(nullptr); //中节点经过过,但还为访问,相当于做标记
if(tmp->left) st.push(tmp->left);
}else{ //结点为null,则是做过标记的之前经历过一次的中节点
st.pop(); //弹出null
tmp=st.top(); //拿到中节点,进行访问
st.pop();
res.push_back(tmp->val);
}
}
return res;
}
前序遍历
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> st;
st.push(root); //根节点进栈
while(!st.empty()){ //栈不空,还有节点未遍历
TreeNode *tmp=st.top();
if(tmp){
st.pop();
if(tmp->right) st.push(tmp->right); //结点逆序进栈
if(tmp->left) st.push(tmp->left);
st.push(tmp);
st.push(nullptr); //做标记,应该访问这个节点的时机
}else{
st.pop();
tmp=st.top();
st.pop();
res.push_back(tmp->val);
}
}
return res;
}
后序遍历
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> st;
st.push(root); //根节点进栈
while(!st.empty()){ //栈不空,还有节点未遍历
TreeNode *tmp=st.top();
if(tmp){
// st.pop();
// st.push(tmp);
st.push(nullptr);//做标记,应该访问这个节点的时机
if(tmp->right) st.push(tmp->right); //结点逆序进栈
if(tmp->left) st.push(tmp->left);
}else{
st.pop();
tmp=st.top();
st.pop();
res.push_back(tmp->val);
}
}
return res;
}