LeetCode【剑指offer】系列(树篇)

剑指offer07.重建二叉树

题目链接

题目:某二叉树的先序遍历结果记录于整数数组preorder,它的中序遍历结果记录于整数数组inorder。请根据preorderinorder的提示构造出这棵二叉树并返回其根节点。注意:preorderinorder中均不含重复数字。

思路:前序遍历的首元素为树的根节点node的值。在中序遍历中搜索根节点node的索引 ,可将中序遍历划分为[ 左子树 | 根节点 | 右子树 ] 。根据中序遍历中的左(右)子树的节点数量,可将 前序遍历 划分为[ 根节点 | 左子树 | 右子树 ]

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
private:
    vector<int> preorder;
    unordered_map<int, int> hmap;

    TreeNode* recur(int root, int left, int right){
        if(left > right)
            return nullptr;
        TreeNode *node = new TreeNode(preorder[root]);
        int idx = hmap[preorder[root]];
        node -> left = recur(root + 1, left, idx - 1);
        node -> right = recur(root + idx - left + 1, idx + 1, right);
        return node;
    }

public:
    TreeNode* deduceTree(vector<int>& preorder, vector<int>& inorder) {
        this -> preorder = preorder;
        for(int i = 0; i < inorder.size(); i++)
            hmap[inorder[i]] = i;
        return recur(0, 0, inorder.size() - 1);
    }
};

剑指offer26.树的子结构

题目链接

题目:给定两棵二叉树tree1tree2,判断tree2是否以tree1的某个节点为根的子树具有相同的结构和节点值 。
注意,空树 不会是以tree1的某个节点为根的子树具有 相同的结构和节点值 。

思路:首先需要一个辅助函数check,用于判断以a为根节点的树是否包含以b为根节点的树。具体判断流程如下:如果b为空,说明b树遍历完了,返回true;如果a为空,说明b包含a没有的结构,返回false。然后判断根节点的val值是否相同,不相同返回false,最后递归遍历两个树的左右子树是否相同。

isSubStructure函数中先序遍历树A,以A的每一个节点为根节点和B做判断。

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool check(TreeNode* pa, TreeNode* pb){
        if(pb == nullptr)
            return true;
        if(pa == nullptr)
            return false;
        if(pa -> val != pb -> val)
            return false;
        return check(pa -> left, pb -> left) && check(pa -> right, pb -> right);
    }

    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(A == nullptr || B == nullptr)
            return false;
        if(check(A, B))
            return true;
        return isSubStructure(A -> left, B) || isSubStructure(A -> right, B);
    }
};

剑指offer27.二叉树的镜像

题目链接

题目:给定一棵二叉树的根节点 root,请左右翻转这棵二叉树,并返回其根节点。

思路:递归

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
TreeNode* flipTree(TreeNode* root) {
    if(root == nullptr)
        return nullptr;
    if(root ->left == nullptr && root -> right == nullptr)
        return root;
    TreeNode *tmp = root -> left;
    root -> left = root -> right;
    root -> right = tmp;
    flipTree(root -> left);
    flipTree(root -> right);
    return root;
}
};

剑指offer28.对称的二叉树

题目链接

题目:请设计一个函数判断一棵二叉树是否轴对称 。

思路:实现这样一个递归函数,通过「同步移动」两个指针的方法来遍历这棵树,p指针和q指针一开始都指向这棵树的根,随后p右移时,q左移,p左移时,q右移。每次检查当前p和q节点的值是否相等,如果相等再判断左右子树是否对称。

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left),
 * right(right) {}
 * };
 */
class Solution {
public:
    bool check(TreeNode* p, TreeNode* q) {
        if (!p && !q)
            return true;
        if (!p || !q)
            return false;
        return p->val == q->val && check(p->left, q->right) &&
               check(p->right, q->left);
    }

    bool checkSymmetricTree(TreeNode* root) { 
        return check(root, root); 
    }
};

剑指offer32-I.从上到下打印二叉树

题目链接

题目:一棵圣诞树记作根节点为root的二叉树,节点值为该位置装饰彩灯的颜色编号。请按照从的顺序返回每一层彩灯编号。

思路:简单的层序遍历

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
vector<int> decorateRecord(TreeNode* root) {
    vector<int> res;
    if(!root)
        return res;
    queue<TreeNode*> q;
    q.push(root);
    while(!q.empty())
        {
            TreeNode *node = q.front();
            q.pop();
            res.push_back(node -> val);
            if(node -> left)
                q.push(node -> left);
            if(node -> right)
                q.push(node -> right);

        }
    return res;
}
};

剑指offer32-II.从上到下打印二叉树

题目链接

题目:一棵圣诞树记作根节点为root的二叉树,节点值为该位置装饰彩灯的颜色编号。请按照从左到右的顺序返回每一层彩灯编号,每一层的结果记录于一行。

思路:仍然是层序遍历。相比上一题需要知道每一层何时结束。在每一层刚开始的时候先记录下队列中元素的个数,这个个数就是该层元素的个数。然后取出相应个数的元素即可。

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> decorateRecord(TreeNode* root) {
        vector<vector<int>> res;
        if(!root)
            return res;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            vector<int> tmp;
            int cnt = q.size();
            for(int i = 0; i < cnt; i++)
            {
                TreeNode *node = q.front();
                q.pop();
                tmp.push_back(node -> val);
                if(node -> left)
                    q.push(node -> left);
                if(node -> right)
                    q.push(node -> right);
            }
            res.push_back(tmp);
        }
        return res;
    }
};

剑指offer32-III.从上到下打印二叉树

题目链接

题目:一棵圣诞树记作根节点为root的二叉树,节点值为该位置装饰彩灯的颜色编号。请按照如下规则记录彩灯装饰结果:

  • 第一层按照从左到右的顺序记录
  • 除第一层外每一层的记录顺序均与上一层相反。即第一层为从左到右,第二层为从右到左。

思路:简单的方法是在上一题的基础上做一个层数的判断,如果是偶数层,然后reverse一下tmp数组。

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> decorateRecord(TreeNode* root) {
        vector<vector<int>> res;
        if(!root)
            return res;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            int cnt = q.size();
            vector<int> tmp;
            for(int i = 0; i < cnt; i++)
            {
                TreeNode *node = q.front();
                q.pop();
                tmp.push_back(node -> val);
                if(node -> left)
                    q.push(node -> left);
                if(node -> right)
                    q.push(node -> right);
            }
            if(res.size() % 2 == 1)
                reverse(tmp.begin(), tmp.end());
            res.push_back(tmp);
        }
        return res;
    }
};

剑指offer33.二叉搜索树的后续遍历序列

题目链接

题目:请实现一个函数来判断整数数组 postorder 是否为二叉搜索树的后序遍历结果。

思路一:遍历后序遍历的 [i,j] 区间元素,寻找第一个大于根节点的节点,索引记为m。此时,可划分出左子树区间[i,m−1]、右子树区间 [m,j−1]、根节点索引j。由于左子树区间都比根节点小,所以左子树区间符合搜索树的正确性。然后遍历右子树,验证是否都比根节点大。最后,左右子树各自也得是二叉搜索树,递归判断即可。

通过代码:

class Solution {
public:
    bool recur(vector<int> & postorder, int i, int j){
        if(i >= j)
            return true;
        int p = i;
        while(postorder[p] < postorder[j])
            p++;
        int m = p;
        while(postorder[p] > postorder[j])
            p++;
        return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
    }

    bool verifyTreeOrder(vector<int>& postorder) {
        return recur(postorder, 0, postorder.size() - 1);
    }
};

剑指offer34.二叉树中和为某一值的路径

题目链接

题目:给你二叉树的根节点root和一个整数目标和targetSum,找出所有从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

思路:递归+回溯,前序遍历。

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void traverse(TreeNode* root, int target, vector<int> path, vector<vector<int>>& res) {
        if(!root)
            return;
        path.push_back(root -> val);
        if(!root -> left && !root -> right && root -> val == target)
        {
            res.push_back(path);
            return;
        }
        traverse(root -> left, target - root -> val, path, res);
        traverse(root -> right, target - root -> val, path, res);
    }

    vector<vector<int>> pathTarget(TreeNode* root, int target) {
        vector<vector<int>> res;
        vector<int> path;
        traverse(root, target, path, res);
        return res;
    }
};

剑指offer36.二叉搜索树与双向链表

题目链接

题目:将一个二叉搜索树就地转化为一个已排序的双向循环链表

对于双向循环列表,你可以将左右孩子指针作为双向循环链表的前驱和后继指针,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

特别地,我们希望可以 就地 完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中最小元素的指针。

思路:二叉搜索树的中序遍历结果就是升序序列。中序遍历的同时,用一个pre指针保存上一个节点,在处理当前节点的时候将pre的右指针指向当前节点,从而建立后继关系;当前节点的左指针指向pre从而建立前驱关系。如果一开始pre为空指针,说明这是首元节点,用一个head保存一下。整个树遍历结束,pre会停留在最后一个节点。此时处理好双向链表的循环关系。

通过代码:

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    Node *pre, *head;

    void traverse(Node* root) {
        if(!root)
            return;
        traverse(root -> left);
        if(pre)
        {
            pre -> right = root;
            root -> left = pre;
        }
        else
            head = root;
        pre = root;
        traverse(root -> right);
    }

    Node* treeToDoublyList(Node* root) {
        if(!root)
            return root;
        traverse(root);
        head -> left = pre;
        pre -> right = head;
        return head;
    }
};

剑指offer37.序列化二叉树

题目链接

题目:序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。

请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。

提示: 输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。

思路:先序遍历这颗二叉树,遇到空子树的时候序列化成None,否则继续递归序列化。那么我们如何反序列化呢?首先我们需要根据“,”把原先的序列分割开来得到先序遍历的元素列表,然后从左向右遍历这个序列:

  • 如果当前的元素为 None,则当前为空树
  • 否则先解析这棵树的左子树,再解析它的右子树

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Codec {
public:

    void rserialize(TreeNode *root, string &ret) {
        if(!root)
        {
            ret += "None,";
            return;
        }
        ret += to_string(root -> val) + ",";
        rserialize(root -> left, ret);
        rserialize(root -> right, ret);
    }

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string ret;
        rserialize(root, ret);
        return ret;
    }

    TreeNode* rdeserialize(deque<string> &dq){
        if(dq.front() == "None")
        {
            dq.pop_front();
            return nullptr;
        }
        TreeNode *root = new TreeNode(stoi(dq.front()));
        dq.pop_front();
        root -> left = rdeserialize(dq);
        root -> right = rdeserialize(dq);
        return root;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        deque<string> dq;
        int start = 0, end = data.find(',');
        while(end != string::npos)
        {
            dq.push_back(data.substr(start, end - start));
            start = end + 1;
            end = data.find(',', start);
        }
        return rdeserialize(dq);
    }
};

// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));

剑指offer54.二叉搜索树的第k大节点

题目链接

题目:某公司组织架构以二叉搜索树形式记录,节点值为处于该职位的员工编号。请返回第cnt大的员工编号。

思路:二叉搜索树的中序遍历(左-根-右)结果为升序序列,所以,我们可以将其倒过来(右-根-左)得到降序序列。在右-根-左遍历的时候cnt依次减1,减到0即为结果,从而提前返回。

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int res, cnt;

    void traverse(TreeNode *root) {
        if(!root)
            return;
        traverse(root -> right);
        cnt--;
        if(cnt == 0)
        {
            res = root -> val;
            return;
        }
        traverse(root -> left);
    }

    int findTargetNode(TreeNode* root, int cnt) {
        this->cnt = cnt;
        traverse(root);
        return res;
    }
};

剑指offer55-I.二叉树的深度

题目链接

题目:某公司架构以二叉树形式记录,请返回该公司的层级数。

思路:层序遍历

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int calculateDepth(TreeNode* root) {
        if(!root)
            return 0;
        queue<TreeNode*> q;
        int depth = 0;
        q.push(root);
        while(!q.empty())
        {
            depth++;
            int cnt = q.size();
            for(int i = 0; i < cnt; i++)
            {
                TreeNode *node = q.front();
                q.pop();
                if(node->left)
                    q.push(node -> left);
                if(node -> right)
                    q.push(node -> right);
            }
        }
        return depth;
    }
};

剑指offer55-II.平衡二叉树

题目链接

题目:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

思路:后序遍历。如果为平衡二叉树,返回其高度;否则,返回-1。通过左右子树的高度差判断当前层级的树是否平衡。

通过代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int traverse(TreeNode *root) {
        if(!root)
            return 0;
        int l = traverse(root -> left);
        int r = traverse(root -> right);
        if(l == -1 || r == -1 || abs(l - r) > 1)
            return -1;
        return max(l, r) + 1;
    }

    bool isBalanced(TreeNode* root) {
        return traverse(root) != -1;
    }
};

剑指offer68-I.二叉搜索树的最近公共祖先

题目链接

题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

思路:因为二叉搜索树是有序的,所以如果root比两个节点都小,说明公共祖先应该在root的右子树;如果root比两个节点都大,说明公共祖先应该在root的左子树。如果root在中间或是等于其中某一节点,那么root就是公共祖先。

通过代码:

/**
 * 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:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root -> val < min(p -> val, q -> val))
            return lowestCommonAncestor(root -> right, p, q);
        if(root -> val > max(p -> val, q -> val))
            return lowestCommonAncestor(root -> left, p, q);
        return root;
    }
};

剑指offer68-II.二叉树的最近公共祖先

题目链接

题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

思路:后序遍历。如果当前根节点为空或者正好等于指定节点,直接返回当前根节点。因为当前节点等于指定节点的话,没有必要再往下递归下去了,即便子树中有另一个指定节点,要返回的公共祖先还是当前节点。如果子树中没有另一个指定节点,就应该把当前节点传递上去。递归调用找到左右子树的公共祖先,如果都不为空,说明两个目标节点就在这两个子树中,最低公共祖先就是当前根节点;如果只有一个不为空,说明此时要么公共祖先已经找到了,该把答案返回去了;要么还有一个目标节点在上层,要返回上去汇合。

通过代码:

/**
 * 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:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root || root == p || root == q)
            return root;
        TreeNode *left = lowestCommonAncestor(root -> left, p, q);
        TreeNode *right = lowestCommonAncestor(root -> right, p, q);
        if(left && right)
            return root;
        else if(!left)
            return right;
        else
            return left;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

h0l10w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值