这次要记录的是做leetbook:二叉树的经历,这是一本很小的book,所以决定用一晚上的时间做完。传送门
树的遍历
t1 二叉树的先序遍历
常规操作,简单题要做难,所以考虑用多种方法来写。
递归法
最常规的递归法,按照根->左->右的顺序来写就可以了。
class Solution {
public:
void solve(TreeNode*root, vector<int>& a) {
if(root == NULL) return;
a.push_back(root->val);
solve(root->left, a);
solve(root->right, a);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ans;
solve(root, ans);
return ans;
}
};
迭代法
迭代法是用栈模拟递归的操作。因为栈具有先入后弹的特性,所以优先压右儿子进栈。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> ans;
st.push(root);
while(!st.empty())
{
TreeNode* tmp = st.top();st.pop();
if(tmp != NULL) ans.push_back(tmp->val);
else continue;
st.push(tmp->right);
st.push(tmp->left);
}
return ans;
}
};
t2 二叉树的中序遍历
递归法
class Solution {
public:
void solve(vector<int>& arr, TreeNode* node) {
if(node == NULL) return;
solve(arr, node->left);
arr.push_back(node->val);
solve(arr, node->right);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
solve(ans, root);
return ans;
}
};
迭代法
中序遍历的迭代实现代码最为复杂。
先序遍历代码简洁的原因是它“遍历”与“处理”的操作同步进行。先序遍历的顺序是根左右,每次先访问根节点然后将其值压入答案数组,接着去访问左右子树。
但中序遍历并不是这样。中序遍历需要首先找到左子树的底部,然后开始按左根右的顺序访问节点。即在找到左子树底部之前的遍历操作中是不含有“压入答案”这个处理步骤的。
解决这个不同步问题的方法就是用一个指针去完成遍历的任务。当它找到了左子树的底部时,就开始操作。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> ans;
TreeNode* pointer = root;
while(pointer != NULL || !st.empty())
{
//去遍历到左子树最底部
if(pointer != NULL)
{
st.push(pointer);
pointer = pointer->left;
}
//摸到最底部,开始操作
else
{
pointer = st.top();st.pop();
ans.push_back(pointer->val);
pointer = pointer->right;
}
}
return ans;
}
};
t3 二叉树的后序遍历
递归法
class Solution {
public:
void solve(vector<int>& arr, TreeNode* node) {
if(node == NULL) return;
solve(arr, node->left);
solve(arr, node->right);
arr.push_back(node->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ans;
solve(ans, root);
return ans;
}
};
迭代法
后序遍历的迭代实现可以通过先序遍历的代码改进得到。
先序遍历的遍历顺序是根->左->右,后序遍历是左->右->根。那么首先调整先序遍历的代码,从 根->左->右改成根->右->左,然后在遍历完整棵树后翻转答案数组,这样就得到了按左->右->根顺序遍历的答案序列了。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> ans;
st.push(root);
while(!st.empty())
{
TreeNode* tmp = st.top();st.pop();
if(tmp != NULL) ans.push_back(tmp->val);
else continue;
st.push(tmp->left);
st.push(tmp->right);
}
reverse(ans.begin(), ans.end());
return ans;
}
};
t4 二叉树的层序遍历
层序遍历顾名思义,一层一层的遍历。一般采用队列实现这个操作,每次对队首节点操作,将其左右子压入队,循环往复即可。
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;
queue<TreeNode*> que;
que.push(root);
if(root == NULL) return ans;
while(!que.empty())
{
int size = que.size();
vector<int> part;
while(size--)
{
TreeNode* node = que.front();que.pop();
part.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
ans.push_back(part);
}
return ans;
}
};
运用递归解决问题
t1 二叉树的最大深度
要求二叉树的最大深度,那么就要自顶向下的找。
如果当前遍历到空节点,直接return;如果当前节点是叶子节点,那么维护ans为当前ans值与depth值之间的最大值,如果都不是,继续递归找左右子树,每次深度+1.
class Solution {
public:
int ans = 0;
void find(TreeNode* node, int depth) {
if(node == NULL) return;
if(node->left == NULL && node->right == NULL)
ans = max(ans, depth);
find(node->left, depth + 1);
find(node->right, depth + 1);
}
int maxDepth(TreeNode* root) {
find(root, 1);
return ans;
}
};
t2 对称二叉树
判断一颗二叉树是否是轴对称的,可以用两个指针,依然是自上而下的遍历,一个往左的时候另一个就往右,如果它们的值到叶子节点都一直相等,显然这棵树就是对称的。
class Solution {
public:
bool check(TreeNode* l, TreeNode* r) {
//同时走到叶子节点,显然对称
if(l == NULL && r == NULL) return true;
//不同时走到叶子节点,不对称
if(l == NULL || r == NULL) return false;
//如果当前两指针指向的节点值相等且它们的左右子树都满足相等,则本身对称
return l->val == r->val && check(l->left, r->right) && check(l->right, r->left);
}
bool isSymmetric(TreeNode* root) {
return check(root, root);
}
};
t3 路径总和
依旧是递归。如果当前节点为空,那么说明整个遍历过程结束都没有达到suum,直接返回false;如果当前节点是叶子节点,则判断当前val是否等于sum值,如果是则返回true,除外直接递归遍历左右子树,返回二者之或。
值得注意的是,每次递归遍历用sum减去当前的val值,这样可以免除使用额外变量记录当前sum值的麻烦。
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(root == NULL) return false;
if(root->left == NULL && root->right == NULL) return root->val == sum;
return hasPathSum(root->left, sum - root->val) ||
hasPathSum(root->right, sum - root->val);
}
};