二叉树的理论基础
平衡二叉搜索树
平衡二叉搜索树:被称为AVL树,具有以下性质:它是一棵空树,它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树高度差的绝对值不超过1,并且左右子树都是一棵平衡二叉树。
二叉树的遍历方式
二叉树主要有两种遍历方式:
- 深度优先遍历(这里的前中后,其实指的就是中间节点的遍历顺序)
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
- 广度优先遍历
- 层次遍历(迭代法)
栈是一种递归的实现结构,也就是前中后遍历的逻辑其实都是可以借助栈使用非递归的方式来实现。
二叉树的定义
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x):val(x),left(NULL),right(NULL){}
};
二叉树的遍历
- 为什么可以用迭代法(非递归的方式)来实现二叉树的前后中序遍历呢?
因为递归的实现就是,每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
二叉树的前序遍历
- 递归写法
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> result;
traversal(root, result);
return result;
}
};
- 非递归写法(迭代法)
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(); // 中
st.pop();
result.push_back(node->val);
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return result;
}
};
二叉树中序遍历
- 递归写法(执行用时4ms,击败39.88%,内存消耗8.2MB,击败50.48%)
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
dfs(root,ans);
return ans;
}
void dfs(TreeNode* root,vector<int>& ans)
{
if(root == nullptr)
{
return ;
}
dfs(root->left,ans); //先遍历左子树
ans.push_back(root->val); //然后访问根节点
dfs(root->right,ans); //再遍历右子树
}
};
- 非递归写法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
TreeNode* p = root;
stack<TreeNode*>stack;
vector<int> ret;
if(root != nullptr){
while(!stack.empty() || p != nullptr){ // 栈不为空或者树还没有遍历完
if(p != nullptr){ // 扫描结点p的所有左下结点并进栈
// 如果右子树的节点P为空,则可以停止并弹出值。
stack.push(p); // 一颗树的左边界进栈,左孩子结点进栈
p = p->left;
}else{
p = stack.top(); // 弹出结点,并记录结点值
stack.pop();
ret.push_back(p->val);
p = p->right; // 处理右子树
}
}
}
return ret;
}
};
二叉树的后序遍历
- 递归写法
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ans;
dfs(root,ans);
return ans;
}
void dfs(TreeNode* root, vector<int>& ans){
if(root == nullptr)
return;
dfs(root->left,ans); //先遍历左子树
dfs(root->right,ans); //再遍历右子树
ans.push_back(root->val); //最后访问根节点
}
};
再来看后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图:
- 非递归写法
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> ans;
if(root ==nullptr) return ans;
st.push(root);
while(st.empty()){
TreeNode* p =st.top();
st.pop()
ans.push_back(p->val);
if(p-left!=nullptr) st.push(p->left);
if(p->right!=nullptr)st.push(p->right);
}
reverse(ans.begin(),ans.end());
return ans;
}
};