杂记杂记,顾名思义,就是把在我个人做题中用到的二叉树相关知识记录一下。我比较笨只能用这种方法,不然记不住
相关题目
力扣94.二叉树的中序遍历
思路
照着中序遍历的顺序(左根右)
1️⃣递归
二叉树中最常用的遍历方式,对我而言最大的难点是题目限制了函数形式vector,按我之前所学的一般是void类型,所以要保证不开额外空间下构造数组存储结果,其实是有越界的风险的
vector<int> inorderTraversal(TreeNode* root) {
if(root == nullptr) return {};
vector<int> lv = inorderTraversal(root->left);
lv.push_back(root->val);
vector<int> rv = inorderTraversal(root->right);
lv.insert(lv.end(), rv.begin(), rv.end());
return lv;
}
但实际只需要另外声明一个新的inorder就行,看到官方题解感觉自己好蠢
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root, res);
return res;
}
void inorder(TreeNode* root, vector<int>& n)
{
if(!root) return;
inorder(root->left, n);
n.push_back(root->val);
inorder(root->right, n);
}
小总结:时间复杂度为O(n)n个节点都会被访问且只访问一次,空间复杂度O(n)空间复杂度取决于递归的栈深度
2️⃣迭代
用栈后进先出的特点存储节点,再用数组记录每个节点的val
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
while(root != nullptr || !stk.empty()){
while(root != nullptr){
stk.push(root);
root = root->left;
}
root = stk.top();
stk.pop();
res.push_back(root->val);
root = root->right;
}
return res;
}
小总结:时间复杂度O(n)n为二叉树节点的个数,二叉树的遍历中每个节点会被访问且只访问一次,空间复杂度O(n)空间复杂度取决于递归的栈深度
3️⃣Morris中序遍历
这是另外一种遍历方法,在这篇日志里记录其中中序遍历的写法,前序遍历和后序遍历以后有机会再补
涉及到前驱节点prodecessor(当前节点记为x左子树中序遍历的最后一个节点)用于关联x和predecessor,具体步骤如下流程图
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
TreeNode* predecessor = nullptr;
while(root != nullptr){
if(root->left != nullptr){
predecessor = root->left;
while(predecessor->right != nullptr && predecessor->right != root){
predecessor = predecessor->right;
//find predecessor
}
if(predecessor->right == nullptr){
predecessor->right = root;
root = root ->left;
}
else{
res.push_back(root->val);
predecessor->right = nullptr;
//cut the line between predecessor and root
root = root ->right;
}
}
else{
res.push_back(root->val);
root = root ->right;
}
}
return res;
}
小总结:时间复杂度O(n),n为二叉树的节点个数,在Morris遍历中每个节点被访问的次数为2,所以实际时间复杂度为O(2n),空间复杂度减少为O(1)这是Morris遍历的一个特点
4️⃣颜色标记法
为了避免使用栈的时候过于复杂且不好记忆"嵌套循环不易理解,一看就懂一写就废",传统递归效率不行。此方法"兼具栈迭代方法的高效又和递归一样能够理解,并且对于前序中序后序写法一致"
颜色标记法,正如其名,使用颜色标记节点状态,新节点为白色,已访问的节点为灰色,如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点,自身,左子节点依次入栈(栈先进后出的特点,入栈顺序和实际遍历顺序相反)
vector<int> inorderTraversal(TreeNode* root)
{
vector<int> res;
stack<pair<TreeNode*, int> > stk;
//pair二元组
stk.push(make_pair(root, 0));
while(!stk.empty()){
int type = stk.top().second;
TreeNode* node = stk.top().first;
stk.pop();
if(node ==nullptr) continue;
if(type == 0){
stk.push(make_pair(node->right, 0));
stk.push(make_pair(node, 1));
stk.push(make_pair(node->left, 0));
}
else{
res.push_back(node->val);
}
}
return res;
}
用到的函数:make_pair()将两个常量打包成二元结构体
小总结:时间复杂度O(n)n为二叉树节点的个数,二叉树的遍历中每个节点会被访问2次,空间复杂度O(n)空间复杂度取决于递归的栈深度