复习一下二叉树的遍历,把力扣相关的题再做一遍,记录一下各种遍历的C++实现方法。
首先明确二叉树节点的定义,就是力扣题给出的:
struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} };返回也按照对应的力扣题用vector了。
前序遍历就是父节点在前面啦,“父-左-右”要不就是“父-右-左”,题目规定是前者,因此这里还是用“父-左-右”吧。对应的力扣题为Leetcode144。
递归方法很简单,其实就是最简单的深度优先搜索。
vector<int> res; vector<int> preorderTraversal(TreeNode* root) { if(root) { res.push_back(root->val); preorderTraversal(root->left); preorderTraversal(root->right); } return res; }递归其实隐式地用到了栈,要是不用递归实现前序遍历,可以显式地用一个辅助栈。
vector<int> preorderTraversal(TreeNode *root) { vector<int> res; stack < TreeNode * > S;//辅助栈 TreeNode *node = root; while (!S.empty() || node != nullptr) { while (node != nullptr) { S.push(node); res.push_back(node->val); node = node->left; } node = S.top()->right; S.pop(); } return res; }中序遍历就是父节点在中间,如按照“左-父-右”的顺序遍历二叉树。对应的力扣题为Leetcode94。
递归方法还是一样的简洁,和前序遍历相比就是记录节点值的时刻换了。
vector<int> res; vector<int> inorderTraversal(TreeNode* root) { if(root) { inorderTraversal(root->left); res.push_back(root->val); inorderTraversal(root->right); } return res; }和前序遍历的代码差不多,无非就是前序是在入栈的时候记录节点的值,而中序是在出栈的时候记录节点的值。
vector<int> inorderTraversal(TreeNode* root) { vector<int> res; stack <TreeNode *> S;//辅助栈 TreeNode *node = root; while (!S.empty() || node != nullptr) { while (node != nullptr) { S.push(node); node = node->left; } res.push_back(S.top() -> val); node = S.top() -> right; S.pop(); } return res; }后序遍历就是父放在最后了,“左-右-父”。对应的力扣题为Leetcode145。
不解释。
vector<int> res; vector<int> postorderTraversal(TreeNode* root) { if(root){ postorderTraversal(root->left); postorderTraversal(root->right); res.push_back(root->val); } return res; }还是有点生疏,到这里卡住了一会,就想到可以用先序遍历(注意是“父-右-左”),然后逆过来。但这样有些讨巧,虽然也把题给做了,这里先记录下这种方法。
vector<int> postorderTraversal(TreeNode* root) { vector<int> res; stack <TreeNode *> S;//辅助栈 TreeNode *node = root; while (!S.empty() || node != nullptr) { while (node != nullptr) { S.push(node); res.push_back(S.top() -> val); node = node->right; } node = S.top() -> left; S.pop(); } reverse(res.begin(), res.end()); return res; }还是感觉这个方法太讨巧了,所以想找别的方法。因此在题解中搜寻,看到一种有意思的方法,这里也记录下来。直接给出原址:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/a-li-mian-shi-ti-zhi-yong-zhan-qu-zuo-er-cha-shu-d/.。这种方法大意是在父节点入栈后再压入一个nullptr作为指示,具体还是看代码好懂。顺带一提,看了巧妙的解法我都不敢写下去了,感觉自己还是差远了,没必要做这个笔记,但是为复习,还是继续写。
2020-9-29更新:今天的力扣每日一题就是这个,官方答案给出的迭代方法很厉害。一种方法是如果root的右儿子不为空,或不是已经遍历了的节点,就再放回去,遍历完右子树的节点再遍历root。另外一种方法用到Morris遍历,Morris遍历我现在还不能参透。
层序遍历,顾名思义就是一层一层地遍历,对应的力扣题为Leetcode102。
感觉层序遍历还是用迭代方法好想一些,如果非要递归也不是不行。一种讨巧的方法是在递归的同时计算层数,然后将节点值放到准确的位置。但这种方法也只能用来做这个题了,和翻转先序的结果以解决后序遍历问题一样,个人感觉这种方法也不算真的在“层序”。
vector<vector<int>> res; void dfs(TreeNode* node, int level){ if(node){ if(level >= res.size()) res.push_back(vector<int>()); res[level].push_back(node -> val); dfs(node -> left, level + 1); dfs(node -> right, level + 1); } } vector<vector<int>> levelOrder(TreeNode* root) { dfs(root, 0); return res; }再次提醒自己遇到层序遍历还是用迭代吧!
层序遍历就不用stack了,用C++的queue(队列)辅助实现,用到是思想是广度优先搜索(BFS)。
vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> res; queue<TreeNode*> Q;//辅助队列 if(root) Q.push(root); while(!Q.empty()){ vector<int> tmp; int N = Q.size(); for(int i = 0; i < N; ++i){ TreeNode* node = Q.front(); tmp.push_back(node -> val); if(node -> left) Q.push(node -> left); if(node -> right) Q.push(node -> right); Q.pop(); } res.push_back(tmp); } return res; }注:好了,就这样了,如果有什么问题,或者说之后想到什么可以改进的地方,那就之后复习再修改吧。