文章目录
方法论:
- 看题五分钟,不会做,看解析;
- 先看中文站,再看国际站;
- 选择最优解析;
- 回头再来写
面试四步走:
- 和面试官,探讨题目限制条件;
- 说说可能解,选择最优解;
- 码字;
- 跑测试用例
关于树,需要了解:
- 树节点定义
- 遍历算法
推荐一个算法可视化的网页 ,VISUALGO-BST,刚看到的时候感觉很惊艳!!!
二叉树节点定义:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
94. 二叉树的中序遍历
可能解:
- 递归算法
- 迭代算法
- 利用栈模拟递归通用模板;
# 1. 递归算法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root, res);
return res;
}
private:
void inorder(TreeNode* root, vector<int>& res) {
if (root == nullptr) return;
inorder(root->left, res);
res.push_back(root->val);
inorder(root->right, res);
}
};
# 2. 迭代算法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sk;
TreeNode* cur = root;
while (cur != NULL || !sk.empty()){
while (cur != NULL){
sk.push(cur);
cur = cur->left;
}
TreeNode* tmp = sk.top();
sk.pop();
res.push_back(tmp->val);
cur = tmp->right;
}
return res;
}
};
# 3. 利用栈模拟递归通用模板
class Solution {
public:
// 写代码,注意顶层设计思想,先写框架,再细节
// 工作中,业务代码同理
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sk;
if (root == nullptr) return {};
sk.push(root);
while (!sk.empty()) {
TreeNode* tmp = sk.top();
sk.pop();
if (tmp != nullptr) {
if (tmp->right) sk.push(tmp->right);
sk.push(tmp);
sk.push(nullptr);
if (tmp->left) sk.push(tmp->left);
} else {
res.push_back(sk.top()->val);
sk.pop();
}
}
return res;
}
};
144. 二叉树的前序遍历
可能解:
- 递归方法
- 迭代法
- 通用模板
# 2. 迭代法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sk;
sk.push(root);
while (!sk.empty()){
TreeNode* tmp = sk.top();
sk.pop();
if (tmp == NULL) continue;
res.push_back(tmp->val);
sk.push(tmp->right);
sk.push(tmp->left);
}
return res;
}
};
# 3. 通用模板法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sk;
if(root == nullptr) return {};
sk.push(root);
while (!sk.empty()){
TreeNode* tmp = sk.top();
sk.pop();
if (tmp != nullptr){
// 压栈的时候与前序遍历的方法,逆序, 右--左--根--nullptr
if (tmp->right) sk.push(tmp->right);
if (tmp->left) sk.push(tmp->left);
sk.push(tmp);
sk.push(nullptr);
} else {
res.push_back(sk.top()->val);
sk.pop();
}
}
return res;
}
};
145.二叉树的后序遍历
可能解法:
- 递归法
- 前序遍历,再进行反转:前序:root->left->right,后序:left->right->root,modify前序:root->right->left;
- 通用模板
# 2. modify前序遍历反转
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sk;
sk.push(root);
while (!sk.empty()){
TreeNode* tmp = sk.top();
sk.pop();
if (tmp == NULL) continue;
res.push_back(tmp->val);
sk.push(tmp->left);
sk.push(tmp->right);
}
reverse(res.begin(), res.end());
return res;
}
};
# 3. 通用模板
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
if(root==NULL) return {};
vector<int> res;
stack<TreeNode*> sk;
sk.push(root);
while (!sk.empty()){
TreeNode* temp = sk.top();
sk.pop();
if (temp != NULL){
sk.push(temp);
sk.push( NULL );
if (temp->right ) {sk.push(temp->right);}
if (temp->left ) {sk.push(temp->left);}
}
else {
res.push_back(sk.top()->val);
sk.pop();
}
}
return res;
}
};
N叉树节点定义:
class Node {
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
589.N叉树的前序遍历
可能解:
- 递归解法
- 迭代法
- 通用模板法
# 1.递归解法
class Solution {
public:
vector<int> preorder(Node* root) {
vector<int> res;
pre(root, res);
return res;
}
private:
void pre(Node* root, vector<int>& res) {
if (root == nullptr) return;
res.push_back(root->val);
for (auto c : root->children) {
pre(c, res);
}
}
};
# 2.迭代法
class Solution {
public:
vector<int> preorder(Node* root) {
vector<int> res;
stack<Node*> sk;
sk.push(root);
while (!sk.empty()) {
Node* tmp = sk.top();
sk.pop();
// 这一行代码如果不要,需要判断root不为nullptr
if (tmp == nullptr) continue;
res.push_back(tmp->val);
for (int i = tmp->children.size() - 1; i >= 0; i-- ) {
sk.push(tmp->children[i]);
}
}
return res;
}
};
590.N叉树的后序遍历
可能解:
- 递归解法
- 迭代法
- 通用模板法
# 1. 递归解法
class Solution {
public:
vector<int> postorder(Node* root) {
vector<int> res;
post(root, res);
return res;
}
private:
void post(Node* root, vector<int>& res) {
if (root == nullptr) return;
for (auto c : root->children) {
post(c, res);
}
res.push_back(root->val);
}
};
# 2. 迭代法
class Solution {
public:
vector<int> postorder(Node* root) {
vector<int> res;
stack<Node*> sk;
sk.push(root);
while (!sk.empty()) {
Node* tmp = sk.top();
sk.pop();
if (tmp == nullptr) continue;
for (auto c : tmp->children) {
sk.push(c);
}
res.push_back(tmp->val);
}
reverse(res.begin(), res.end());
return res;
}
};
429. N叉树的层序遍历
可能解:
- 利用队列queue实现层次遍历
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
// 1. 需要
if (root == nullptr) return vector<vector<int>>();
vector<vector<int>> res;
queue<Node*> qu;
qu.push(root);
while (!qu.empty()) {
int size = qu.size();
vector<int> curLevel;
for (int i = 0; i < size; i++){
Node* tmp = qu.front();
qu.pop();
curLevel.push_back(tmp->val);
// 2. 这一段直接屏蔽了,qu中间的元素不为nullptr
for (auto c : tmp->children) {
qu.push(c);
}
}
res.push_back(curLevel);
}
return res;
}
};
总结
- 关于树遍历的迭代写法,注意以下三种:前序:root --> left --> right, 中序:left–>root–>right,后序:left–>right–>root;
- 除了中序遍历,其他迭代方法,根节点入栈或队列,while遍历栈或队列是否为空,循环内判断top或front元素是否为nullptr,剩下的就是遍历顺序了
- 中序遍历不把root加入栈中,因为,我们需要先左子树,当节点左结束访问完后,再访问 右节点 的左子树,因此需要一个while上一层的变量。
参考
- 极客时间-算法训练营-覃超
- leetcode中文站
- leetcode国际站(将力扣中文链接,后面的-cn去掉,就是该题的国际站)
- Basic C++ iterative solution with detailed explanations. Super easy for beginners.