刷题记录-树,二叉树

树,二叉树

  • 剑指Offer(三十五):复杂链表的复制(重要)
  • 剑指Offer(三十六):二叉搜索树与双向链表(重要)
  • 剑指Offer(三十七):序列化二叉树(重要)
  • 剑指Offer(五十四):二叉搜索树的第k个结点(重要)
  • 剑指Offer(五十五):二叉树的深度(重要)
  • 剑指Offer(五十五):平衡二叉树(重要)
  • LeetCode(94):二叉树的中序遍历(重要)
  • LeetCode(144):二叉树的前序遍历(重要)
  • LeetCode(145):二叉树的后序遍历(重要)
  • LeetCode(98):验证搜索二叉树(重要)
  • LeetCode(199):二叉树的右视图(重要,BFS)
  • LeetCode(543):二叉树的直径(重要,利用二叉树的深度公式)
  • LeetCode(236):二叉树的最近公共祖先(重要)
  • LeetCode(572):另一个树的子树(重要,递归)
  • 剑指Offer(三十四):二叉树中和为某一值的路径(重要)
  • 剑指Offer(三十三):二叉搜索树的后序遍历序列(重要)
  • 剑指Offer(三十二):之字形打印二叉树(重要,利用双端队列实现前取后放,后取前放)
  • 剑指Offer(三十二):分行从上往下打印二叉树(重要)
  • 剑指Offer(二十八):对称的二叉树(重要)
  • 剑指Offer(二十七):二叉树的镜像(重要)
  • 剑指Offer(二十六):树的子结构(重要)
  • 剑指Offer(七):重建二叉树(重要)
  • 剑指Offer八:二叉树的下一个节点(重要)

剑指Offer(三十五):复杂链表的复制(重要)

面试题35. 复杂链表的复制
难度中等46
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

提示:
• -10000 <= Node.val <= 10000
• Node.random 为空(null)或指向链表中的节点。
• 节点数目不超过 1000 。
三步法:
1、把复制的结点链接在原始链表的每一对应结点后面
2、把复制的结点的random指针指向被复制结点的random指针的下一个结点
3、拆分成两个链表,奇数位置组成原链表,偶数位置组成复制链表

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/
class Solution {
     
public:
    Node* copyRandomList(Node* head) {
     
        //1、把复制的结点链接在原始链表的每一对应结点后面 //复制next 如原来是A->B->C 变成A->A'->B->B'->C->C'
        if(head==NULL) return NULL;
        Node* node=head;
        while(node){
     
            Node* tmp=new Node(node->val);
            tmp->next=node->next;
            node->next=tmp;
            node=tmp->next;
        }
        //2、把复制的结点的random指针指向被复制结点的random指针的下一个结点 //复制random:cur是原来链表的结点 cur->next是复制cur的结点
        node=head;
        while(node){
     
            Node* temp=node->next;
            if(node->random){
     
                temp->random=node->random->next;
            }
            node=temp->next;

        }
        // 拆分链表,将链表拆分为原链表和复制后的链表
        node=head;
        Node* p=head->next;
        while(node){
     
            Node* temp=node->next;
            node->next=temp->next;
            if(temp->next!=NULL){
     
                temp->next=temp->next->next;
            }
            node=node->next;
        }
        return p;
        
    }
};

剑指Offer(三十六):二叉搜索树与双向链表(重要)

面试题36. 二叉搜索树与双向链表
难度中等54收藏分享切换为英文关注反馈
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
方法一:中序遍历

/*
// 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* treeToDoublyList(Node* root) {
     
        if(root == NULL)
            return NULL;
        Node* head = NULL;
        Node* pre = NULL;// 定义一个pre指针
        helper(root,head,pre);
        head->left = pre;
        pre->right = head;
        return head;
    }
    void helper(Node* root,Node* &head,Node* &pre){
     // 中序遍历
        if(root==NULL) return;
        helper(root->left,head,pre);// 先递归遍历左子树

        if(head==NULL){
     
            head=root;
            pre=root;
        }
        else
        {
     
            root->left=pre;// 修改为双向链表
            pre->right=root;
            pre=root;//更新双向链表尾结点
        }

        helper(root->right,head,pre);// 再递归遍历右子树
    }
};

剑指Offer(三十七):序列化二叉树(重要)

面试题37. 序列化二叉树
难度困难37收藏分享切换为英文关注反馈
请实现两个函数,分别用来序列化和反序列化二叉树。
示例:
你可以将以下二叉树:

1

/
2 3
/
4 5
序列化为 “[1,2,3,null,null,4,5]”

/**
 * 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:
    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
     
        if (root == NULL)
            return "#_";
        string res = to_string(root->val) + "_";
        res += serialize(root->left);
        res += serialize(root->right);
        return res;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
     
        std::stringstream ss(data);
        std::string item;
        queue<string> q;
        while (std::getline(ss, item, '_')) 
            q.push(item);
        return helper(q);
    }
    TreeNode* helper(queue<string>& q)
    {
     
        string val = q.front();
        q.pop();
        if (val == "#")
            return NULL;
        TreeNode* head = new TreeNode(stoi(val));
        head->left = helper(q);
        head->right = helper(q);
        return head;
    }
};

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

剑指Offer(五十四):二叉搜索树的第k个结点(重要)

面试题54. 二叉搜索树的第k大节点
难度简单32
给定一棵二叉搜索树,请找出其中第k大的节点。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/
1 4

2
输出: 4
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/
3 6
/
2 4
/
1
输出: 4
限制:
1 ≤ k ≤ 二叉搜索树元素个数
MySolution:

/**
 * 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:
    void dfs(TreeNode *root,vector<int> & res){
     
        if(root==NULL) return;
        if(root->left) dfs(root->left,res);
        res.push_back(root->val);
        if(root->right) dfs(root->right,res);
    }
    int kthLargest(TreeNode* root, int k) {
     
        vector<int> res;
        dfs(root,res);
        return res[res.size()-k];

    }
};

方法一:递归

/**
 * 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:
int res=0;
int cnt=0;

void dfs(TreeNode* root,int k){
     
    if(root==NULL) return;
    dfs(root->right,k);
    if(++cnt==k){
     
        res=root->val;
        return;
    }
    dfs(root->left,k);
}
    int kthLargest(TreeNode* root, int k) {
     
        if(root==NULL||k<1) return 0;
        dfs(root,k);
        return res;

    }
};

方法二:迭代

/**
 * 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:
    int kthLargest(TreeNode* root, int k) {
     
        int n=0;
        stack<TreeNode*> s;
        TreeNode* p = root;
        while (!s.empty() || p!=NULL)
        {
     
            while(p!=NULL)
            {
     
                s.push(p);
                p = p->right;
            }
            p =s.top();
            s.pop();
            if (++n==k)
            {
     
                return p->val;
            }
            p = p->left;
        }
        return 0;

    }
};

剑指Offer(五十五):二叉树的深度(重要)

面试题55 - I. 二叉树的深度
难度简单25
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
提示:

  1. 节点总数 <= 10000
    推荐用递归,取左右子树最大的深度加上1
    两种方法:可以是递归的方法,属于DFS(深度优先搜索);另一种方法是按照层次遍历,属于BFS(广度优先搜索)。
    Mysolution:
/**
 * 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:
    int maxDepth(TreeNode* root) {
     
        if(root==NULL) return 0;
        return max(maxDepth(root->left),maxDepth(root->right))+1;

    }
};

方法

/**
 * 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:
    int maxDepth(TreeNode* root) {
     
        if(root==NULL) return 0;
        queue<TreeNode*> q;
        q.push(root);
        int depth=0;
        while(!q.empty()){
     
            int len=q.size();
            TreeNode* tmp;
            
            int i=0;
            while(i++<len){
     
                tmp=q.front();
                q.pop();
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            depth++;
        }
        return depth;

    }
};

剑指Offer(五十五):平衡二叉树(重要)

面试题55 - II. 平衡二叉树
难度简单29
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/
9 20
/
15 7
返回 true 。

示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/
2 2
/
3 3
/
4 4
返回 false 。

限制:
• 1 <= 树的结点个数 <= 10000
Mysolution:

/**
 * 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:
    int treeDepth(TreeNode* root){
     
        if(root==NULL) return 0;
        return max(treeDepth(root->left),treeDepth(root->right))+1;
    }
    bool isBalanced(TreeNode* root) {
     
        if(root==NULL) return true;
        int leftDepth=treeDepth(root->left);
        int rigthDepth=treeDepth(root->right);
        if(leftDepth-rigthDepth>1||leftDepth-rigthDepth<-1) return false;
        return isBalanced(root->left)&&isBalanced(root->right);

    }
};

解题思路有两种,只遍历一次的方法最优。
重复遍历多次:自顶向下
时间复杂度O(nlogn), 空间复杂度O(n)复杂度分析
自顶向下在遍历树的每个结点的时候,调用函数TreeDepth得到它的左右子树的深度。如果每个结点的左右子树的深度相差都不超过1,则这是一颗平衡的二叉树。这种方法的缺点是,首先判断根结点是不是平衡的,需要使用TreeDepth获得左右子树的深度,然后还需要继续判断子树是不是平衡的,还是需要使用TreeDepth获得子树的左右子树的深度,这样就导致了大量的重复遍历。
只遍历一次:自底向上
时间复杂度O(n), 空间复杂度O(n)复杂度分析
自底向上与自顶向下的逻辑相反,首先判断子树是否平衡,然后比较子树高度判断父节点是否平衡。检查子树是否平衡。如果平衡,则使用它们的高度判断父节点是否平衡,并计算父节点的高度。自底向上计算,每个子树的高度只会计算一次。可以递归先计算当前节点的子节点高度,然后再通过子节点高度判断当前节点是否平衡,从而消除冗余。
方法二:

class Solution {
     
public:

    bool isBalanced(TreeNode* root) {
     
        if (root == NULL)
            return true;
        int depth = 0;
        return helper(root, depth);
    }
    bool helper(TreeNode* root, int &depth)
    {
     
        if (root == NULL)
        {
     
            depth = 0;
            return true;
        }
        int left, right;
        if (helper(root->left, left) && helper(root->right, right) && abs(left-right)<=1)
        {
     
            depth = max(left,right)+1;
            return true;
        }
        return false;
    }
};

LeetCode(94):二叉树的中序遍历(重要)

  1. 二叉树的中序遍历
    难度中等531
    给定一个二叉树,返回它的中序 遍历。
    示例:
    输入: [1,null,2,3]
    1

    2
    /
    3

输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
递归与迭代两种方法
方法一:递归
中序遍历:左子树—> 根结点 —> 右子树,比较容易利用递归写出
方法二:迭代
根据中序遍历的顺序,优先访问左子树,然后在访问根节点和右子树。所以,对于任意结点node,第一部分即直接访问之,之后在判断左子树是否为空,不为空时即重复上面的步骤,直到其为空。若为空,则需要访问右子树。注意,在访问过左孩子之后,需要反过来访问其右孩子,所以,需要栈这种数据结构的支持。对于任意一个结点node,具体步骤如下:
a)访问之,并把结点node入栈,当前结点置为左孩子;
b)判断结点node是否为空,若为空,则取出栈顶结点并出栈,将右孩子置为当前结点;否则重复a)步直到当前结点为空或者栈为空(可以发现栈中的结点就是为了访问右孩子才存储的)
/**

  • 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> res;
    void dfs(TreeNode* root){
     
        if(root==NULL) return;
        dfs(root->left);
        res.push_back(root->val);
        dfs(root->right);
    }
    vector<int> inorderTraversal(TreeNode* root) {
     
        dfs(root);
        return res;              
    }
};
//方法二:迭代
class Solution {
     
public:
    vector<int> inorderTraversal(TreeNode* root) {
     
/*栈S;
p= root;
while(p || S不空){
    while(p){
        p入S;
        p = p的左子树;
    }
    p = S.top 出栈;
    访问p;
    p = p的右子树;
}
*/
        stack<TreeNode*> s;
        vector<int> res;
        if(root==NULL) return res;
        TreeNode* cur=root;
        //s.push(root);
        while(!s.empty()||cur!=NULL){
     
            while(cur!=NULL){
     
                s.push(cur);
                cur=cur->left;
            }
            cur=s.top();
            s.pop();
            res.push_back(cur->val);
            cur=cur->right;
        }
        return res;
        
    }
};

LeetCode(144):二叉树的前序遍历(重要)

  1. 二叉树的前序遍历
    难度中等280
    给定一个二叉树,返回它的 前序 遍历。
    示例:
    输入: [1,null,2,3]
    1

    2
    /
    3

输出: [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
/**

  • 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:
    void dfs(TreeNode* root,vector<int> &res){
     
        if(root==NULL) return;
        res.push_back(root->val);
        dfs(root->left,res);
        dfs(root->right,res);
    }
    vector<int> preorderTraversal(TreeNode* root) {
     
        vector<int> res;
        if(root==NULL) return res;
        dfs(root,res);
        return res;

    }
};
方法二:迭代
/**
 * 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> preorderTraversal(TreeNode* root) {
     
/*栈S;
p= root;
while(p || S不空){
    while(p){
        访问p节点;
        p的右子树入S;
        p = p的左子树;
    }
    p = S栈顶弹出;
}
*/
    vector<int> res;
    if(root==NULL) return res;
    stack<TreeNode*> s;
    TreeNode* p=root;
    while(p||!s.empty()){
     
        while(p){
     
            res.push_back(p->val);
            s.push(p->right);
            p=p->left;
        }
        p=s.top();
        s.pop();
    }
    return res;

    }
};

迭代法:

class Solution {
     
public:
    vector<int> preorderTraversal(TreeNode* root) {
     
        vector<int> res;
        if (root==NULL)
            return res;
        stack<TreeNode*> s;
        s.push(root);
        while (!s.empty())
        {
     
            TreeNode* node = s.top();
            s.pop();
            res.push_back(node->val);
            if (node->right)
            {
     
                s.push(node->right);
            }
            if (node->left)
            {
     
                s.push(node->left);
            }
            
        }
        return res;
    }
};

LeetCode(145):二叉树的后序遍历(重要)

  1. 二叉树的后序遍历
    难度困难327收藏分享切换为英文关注反馈
    给定一个二叉树,返回它的 后序 遍历。
    示例:
    输入: [1,null,2,3]
    1

    2
    /
    3

输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
递归与迭代两种方法
方法一:递归
后序遍历:左子树 —> 右子树 —> 根结点,比较容易利用递归写出
方法二:迭代
使用栈(先进后出)来完成,我们先将根节点放入栈中,从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压左子树再压右子树,然后在调用reverse函数。
使用一个指针lastVisited记录最后访问的节点,一个根节点被访问的前提是:无右子树或右子树已被访问过。/**

  • 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:
    void dfs(TreeNode* root,vector<int> &res){
     
        if(root==NULL) return;
        dfs(root->left,res);
        dfs(root->right,res);
        res.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
     
        vector<int> res;
        if(root==NULL) return res;
        dfs(root,res);
        return res;

    }
};

方法二:迭代

//前序遍历之后reverse
class Solution {
     
public:
    vector<int> postorderTraversal(TreeNode* root) {
     
        vector<int> res;
        if(root==NULL) return res;
        stack<TreeNode*> s;
        TreeNode* p=root;
        while(!s.empty()||p){
     
            while(p){
     
                res.push_back(p->val);
                s.push(p->left);
                p=p->right;
            }
            p=s.top();
            s.pop();

        }
        reverse(res.begin(),res.end());
        return res;

    }
};

按照左子树-根-右子树的方式,将其转换成迭代方式。
思路:每到一个节点 A,因为根要最后访问,将其入栈。然后遍历左子树,遍历右子树,最后返回到 A。
但是出现一个问题,无法区分是从左子树返回,还是从右子树返回。
因此,给 A 节点附加一个标记T。在访问其右子树前,T 置为 True。之后子树返回时,当 T 为 True表示从右子树返回,否则从左子树返回。
当 T 为 false 时,表示 A 的左子树遍历完,还要访问右子树。
同时,当 T 为 True 时,表示 A 的两棵子树都遍历过了,要访问 A 了。并且在 A 访问完后,A 这棵子树都访问完成了。
栈S;

p= root;
T<节点,True/False> : 节点标记;
while(p || S不空){
while§{
p入S;
p = p的左子树;
}
while(S不空 且 T[S.top] = True){
访问S.top;
S.top出S;
}
if(S不空){
p = S.top 的右子树;
T[S.top] = True;
}
}

class Solution {
     
public:
    vector<int> postorderTraversal(TreeNode* root) {
     
        vector<int> res;
        if (root==NULL) return res;
        stack<TreeNode*> s;
        TreeNode* p = root;
        TreeNode* lastVisited = NULL;
        while(!s.empty() || p)
        {
     
            while(p)
            {
     
                s.push(p);
                p = p->left;
            }
            p = s.top();
            if (p->right == NULL || p->right == lastVisited)
            {
     
                res.push_back(p->val);
                s.pop();
                lastVisited = p;
                p = NULL; // p 已经访问过了,没用了设置为NULL
            }
            else
            {
     
                p = p->right;
            }
        }
        return res;

    }
};

LeetCode(98):验证搜索二叉树(重要)

  1. 验证二叉搜索树
    难度中等620收藏分享切换为英文关注反馈
    给定一个二叉树,判断其是否是一个有效的二叉搜索树。
    假设一个二叉搜索树具有如下特征:
    • 节点的左子树只包含小于当前节点的数。
    • 节点的右子树只包含大于当前节点的数。
    • 所有左子树和右子树自身必须也是二叉搜索树。
    示例 1:
    输入:
    2
    /
    1 3
    输出: true
    示例 2:
    输入:
    5
    /
    1 4
    /
    3 6
    输出: false
    解释: 输入为: [5,1,4,null,null,3,6]。
    根节点的值为 5 ,但是其右子节点值为 4 。
    二叉搜索树的中序遍历为升序,中序遍历搜索树后判断 是否是严格升序
    方法一:
/**
 * 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:
    void dfs(TreeNode* root,vector<int> &res){
     
        if(root==NULL) return;
        dfs(root->left,res);
        res.push_back(root->val);
        dfs(root->right,res);
    }
    bool isValidBST(TreeNode* root) {
     
        vector<int> res;
        dfs(root,res);
        set<int> t(res.begin(),res.end());
        vector<int> tmp;
        tmp.assign(t.begin(),t.end());
        return tmp==res;

        return true;
        
    }
};

方法二:

/**
 * 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* pre=NULL;
    bool isValidBST(TreeNode* root) {
     
        if(root==NULL) return true;
        if(!isValidBST(root->left)) return false;// 访问左子树, 如果左子树为false 返回false
        if(pre&&pre->val>=root->val) return false; // 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回 false;否则继续遍历。
        pre=root;// 更新前一个节点
        //if(!isValidBST(root->right)) return false;// 访问右子树
        return isValidBST(root->right);
        
    }
};

LeetCode(199):二叉树的右视图(重要,BFS)

  1. 二叉树的右视图
    难度中等256收藏分享切换为英文关注反馈
    给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
    示例:
    输入: [1,2,3,null,5,null,4]
    输出: [1, 3, 4]
    解释:

1 <—
/
2 3 <—
\
5 4 <—
方法0:

/**
 * 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> rightSideView(TreeNode* root) {
     
        if(root==NULL) return vector<int>();
        vector<int> res;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
     
            int len=q.size();
            res.push_back(q.back()->val);
            int i=0;
            TreeNode* tmp;
            while(i<len){
     
                tmp=q.front();
                q.pop();
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
                i++;
            }
        }
        return res;

    }
};

方法二:
BFS
按层来遍历,先遍历每一层的右子树

class Solution {
     
public:
    vector<int> rightSideView(TreeNode* root) {
     
        if(root==NULL) return vector<int>();
        vector<int> res;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
     
            int len=q.size();
            res.push_back(q.front()->val);
            int i=0;
            TreeNode* tmp;
            while(i<len){
     
                tmp=q.front();
                q.pop();
                if(tmp->right) q.push(tmp->right);
                if(tmp->left) q.push(tmp->left);
                
                i++;
            }
        }
        return res;

    }
};

DFS
按照根节点-右子树-左子树来遍历,逆前序遍历

/**
 * 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:
    void dfs(TreeNode* root,int depth,vector<int> &res){
     
        if(root==NULL) return;
        if(depth==res.size()){
     
            res.push_back(root->val);// 利用结果res的size等于tree的高度的性质
        } 
        dfs(root->right,depth+1,res);
        dfs(root->left,depth+1,res);
    }
    vector<int> rightSideView(TreeNode* root) {
     
        vector<int> res;
        dfs(root,0,res);
        return res;

    }
};

LeetCode(543):二叉树的直径(重要,利用二叉树的深度公式)

  1. 二叉树的直径
    难度简单378收藏分享切换为英文关注反馈
    给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
    示例 :
    给定二叉树
    1
    /
    2 3
    / \
    4 5
    返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
    注意:两结点之间的路径长度是以它们之间边的数目表示。
    二叉树的直径不一定过根节点,因此需要去搜一遍所有子树(例如以root,root.left, root.right…为根节点的树)对应的直径,取最大值。
    root的直径 = root左子树高度 + root右子树高度
    root的高度 = max {root左子树高度, root右子树高度} + 1 (公式记住)
/**
 * 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:
    int res=0;
    int dfs(TreeNode* root){
     // 函数dfs的作用是:找到以root为根节点的二叉树的最大深度
        if(root==NULL) return 0;
        int leftDepth=dfs(root->left);
        int rightDepth=dfs(root->right);
        res=max(res,leftDepth+rightDepth);
        return max(leftDepth,rightDepth)+1;

    }
    int diameterOfBinaryTree(TreeNode* root) {
     
        if(root==NULL) return res;
        dfs(root);
        return res;
    }
};

LeetCode(236):二叉树的最近公共祖先(重要)

  1. 二叉树的最近公共祖先
    难度中等604收藏分享切换为英文关注反馈
    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
    百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
    例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:
• 所有节点的值都是唯一的。
• p、q 为不同节点且均存在于给定的二叉树中。
递归
考虑通过递归对二叉树进行后序遍历,当遇到节点 p 或 q 时返回。从底至顶回溯,当节点 p, q, 在节点 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==NULL ||root==p || root==q) return root;
        TreeNode* left = lowestCommonAncestor(root->left,p,q);
        TreeNode* right = lowestCommonAncestor(root->right,p,q);
        if (left == NULL && right == NULL) return NULL;  // 左右子树同时为空,都不包含p,q,返回NULL
        if (left==NULL) return right;  // 当 left为空 ,right不为空 :p,q都不在root的左子树中,直接返回 right
        if (right==NULL) return left;  // 与上一条件类似
        return root;  // 同时不为空,说明p,q在左右子树异侧,返回root
        
    }
};

LeetCode(572):另一个树的子树(重要,递归)

  1. 另一个树的子树
    难度简单296收藏分享切换为英文关注反馈
    给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
    示例 1:
    给定的树 s:
    3
    /
    4 5
    /
    1 2
    给定的树 t:
    4
    /
    1 2
    返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。
    示例 2:
    给定的树 s:
    3
    /
    4 5
    /
    1 2
    /
    0
    给定的树 t:
    4
    /
    1 2
    返回 false。
    判断镜像二叉树类似,一个树是另一个树的子树 则
    要么这两个树相等
    要么这个树是左树的子树
    要么这个树hi右树的子树
/**
 * 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 helper(TreeNode* s, TreeNode* t){
     
    if(s==NULL&&t==NULL) return true;
    /*
    if(s&&t&&s->val==t->val) return helper(s->left,t->left)&&helper(s->right,t->right);
    else return false;
    */
    return s&&t && s->val==t->val && helper(s->left, t->left) && helper(s->right,t->right);

}
    bool isSubtree(TreeNode* s, TreeNode* t) {
     
        if(s==NULL&&t!=NULL) return false;
        if(s==NULL&&t==NULL) return true;
        return helper(s,t)||isSubtree(s->left,t)||isSubtree(s->right,t);

        
    }
};

剑指Offer(三十四):二叉树中和为某一值的路径(重要)

面试题34. 二叉树中和为某一值的路径
难度中等42
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/
4 8
/ /
11 13 4
/ \ /
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
示:

  1. 节点总数 <= 10000
    典型的带记忆的DFS来解决
    思路:
    递归前序遍历树, 把结点加入路径。
    若该结点是叶子结点则比较当前路径和是否等于期待和。
    弹出结点,每一轮递归返回到父结点时,当前路径也应该回退一个结点
/**
 * 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:
    void dfs(TreeNode* root, int sum,vector<vector<int>> &res,vector<int> &out){
     
        if(root==NULL) return ;
        out.push_back(root->val);
        // 如果是叶节点,并且路径上的节点值的和为输入的值,就像结果中添加这一out
        if(sum==root->val&&root->left==NULL&&root->right==NULL){
     
            res.push_back(out);
        }// 不是叶节点就遍历他的子节点
        dfs(root->left,sum-root->val,res,out);
        dfs(root->right,sum-root->val,res,out);
        out.pop_back();// 到这一步说明不满足要求,要返回父节点,需要删除路径上的当前节点

    }
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
     
        vector<vector<int>> res;
        vector<int> out;
        dfs(root,sum,res,out);
        return res;

    }
};

剑指Offer(三十三):二叉搜索树的后序遍历序列(重要)

面试题33. 二叉搜索树的后序遍历序列
难度中等56
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

参考以下这颗二叉搜索树:
5
/
2 6
/
1 3
示例 1:
输入: [1,6,3,2,5]
输出: false
示例 2:
输入: [1,3,2,6,5]
输出: true

提示:

  1. 数组长度 <= 1000
    已知条件:后序序列最后一个值为root;二叉搜索树左子树值都比root小,右子树值都比root大。
    1、确定root;
    2、遍历序列(除去root结点),找到第一个大于root的位置,则该位置左边为左子树,右边为右子树;
    3、遍历右子树,若发现有小于root的值,则直接返回false;
    4、分别判断左子树和右子树是否仍是二叉搜索树(即递归步骤1、2、3)。
class Solution {
     
public:

    bool helper(vector<int>& postorder,int start,int end){
     
        if(start>end) return false;
        int root=postorder[end];
        int i=start;
        while(i<end){
     
            if(postorder[i]>root){
     
                break;
            }
            i++;
        }
        for(int j=i;j<end;j++){
     
            if(postorder[j]<root) return false;
        }
        //判断左子树是不是二叉搜索树
        bool left = true;
        if (i>start)
        {
     
            left = helper(postorder, start, i-1);
        }
        
        //判断右子树是不是二叉搜索树
        bool right = true;
        if (i < end-1)
        {
     
            right = helper(postorder, i, end-1);
        }
        return left && right;
        //return helper(postorder,start,i-1)&&helper(postorder,i,end-1);

    }
    bool verifyPostorder(vector<int>& postorder) {
     
        bool res=true;
        if(postorder.empty()) return res;
        int len=postorder.size();
        res=helper(postorder,0,len-1);
        return res;

    }
};

剑指Offer(三十二):之字形打印二叉树(重要,利用双端队列实现前取后放,后取前放)

面试题32 - III. 从上到下打印二叉树 III
难度中等19
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:
[
[3],
[20,9],
[15,7]
]

提示:

  1. 节点总数 <= 1000
    因为奇偶层的打印顺序不一样是相反的,可以用reverse来解决,但是海量数据时这个效率很低。

因为奇数层的打印顺序是从左到右,偶数层的打印顺序是从右到左,可以利用STL容器deque中push_back(),push_front(),front(),back(),pop(),popfront()来解决,实现前取后放,后取前放,因为deque队列中的元素,都是从左到右的,当zigzag==true;,从左到右打印,就从前边开始取出元素,下一层的元素从后边压入,同理,从右向左打印时,从后边取出元素,下一层的元素就从前边压入,注意先压入右子树的,后压入左子树,这样新的队列还是从左到右,然后继续,直至队列为空。

/**
 * 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<vector<int>> levelOrder(TreeNode* root) {
     
        vector<vector<int>> res;
        if(root==NULL) return res;
        deque<TreeNode*> dq;
        dq.push_front(root);
        int level=0;
        while(!dq.empty()){
     
            int len=dq.size();
            vector<int> temp(len);
            TreeNode* tmp;
            for(int i=0;i<len;++i){
     //注意&&优先级,加括号!!!
                if((level&1)==0){
     //前取后放:从左向右,所以从前边取,后边放入
                    tmp=dq.front();
                    dq.pop_front();
                    temp[i]=tmp->val;
                    if(tmp->left) dq.push_back(tmp->left);
                    if(tmp->right) dq.push_back(tmp->right);
                }else{
     // 后取前放:从右向左,从后边取,前边放入
                    tmp=dq.back();
                    dq.pop_back();
                    temp[i]=tmp->val;
                    if(tmp->right) dq.push_front(tmp->right);
                    if(tmp->left) dq.push_front(tmp->left);
                }
                
                
            }
            level++;
            res.push_back(temp);

        }
        return res;

    }
};

剑指Offer(三十二):分行从上往下打印二叉树(重要)

面试题32 - II. 从上到下打印二叉树 II
难度简单25
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]

提示:

  1. 节点总数 <= 1000
    方法一:迭代
    同样借助队列实现,不过注意,与之前不同,此题要注意计算出当前层有多少个元素:等于队列的长度,按照顺序遍历完队列加入out中。
    第 0 层只包含根节点 root,算法实现如下:
    • 初始化队列只包含一个节点 root。
    • 当队列非空的时候:
    o 计算当前层有多少个元素:等于队列的长度。
    o 初始化一个空列表out
    o 利用循环将这些元素从队列中弹出,并加入out空列表中。
    o 将他们的孩子节点作为下一层压入队列中。
    o 将out列表加入res中,进入下一层。
    方法二:递归
    递归,首先确认树非空,然后调用递归函数 helper(node, level),参数是当前节点和节点的层次。程序过程如下:
    • 输出为 res,当前最高层数就是res的长度 res.size()。比较访问节点所在的层次 level 和当前最高层次 res.size() 的大小(当前所在层次level始终是不大于当前最高层次res.size()的),如果level == res.size()了,就向res添加一个空res.push_back(vector())。
    • 将当前节点插入到对应层的列表 res[level] 中。
    • 递归非空的孩子节点:helper(node->left / node->right, level + 1)。
    方法一:队列
/**
 * 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<vector<int>> levelOrder(TreeNode* root) {
     
        vector<vector<int>> res;
        if(root==NULL) return res;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
     
            int len=q.size();
            vector<int> temp(len);
            TreeNode* tmp;
            for(int i=0;i<len;++i){
     
                tmp=q.front();
                q.pop();
                temp[i]=tmp->val;
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);

            }
            res.push_back(temp);
        }
        return res;

    }
};

方法二:递归

/**
 * 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<vector<int>> res;
    void helper(TreeNode* root,int level){
     
        if(root==NULL) return;
        if(res.size()==level){
     
            res.push_back(vector<int>());
        }
        res[level].push_back(root->val);
        if(root->left) helper(root->left,level+1);
        if(root->right) helper(root->right,level+1);

    }

    vector<vector<int>> levelOrder(TreeNode* root) {
     
        
        if(root==NULL) return res;
        helper(root,0);
        return res;

    }
};

剑指Offer(二十八):对称的二叉树(重要)

面试题28. 对称的二叉树
难度简单42
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/
2 2
/ \ /
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/
2 2
\
3 3

示例 1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:
输入:root = [1,2,2,null,3,null,3]
输出:false

限制:
0 <= 节点个数 <= 1000
思路:递归
1.怎么判断一棵树是不是对称二叉树? 答案:如果所给根节点,为空,那么是对称。如果不为空的话,当他的左子树与右子树对称时,他对称
2.那么怎么知道左子树与右子树对不对称呢?在这我直接叫为左树和右树 答案:如果左树的左孩子与右树的右孩子对称,左树的右孩子与右树的左孩子对称,那么这个左树和右树就对称。
那用递归分别比较左右子树
递归结束条件:
都为空指针则返回 true
只有一个为空则返回 false
两个指针当前节点值不相等 返回false
递归过程:
判断 A 的右子树与 B 的左子树是否对称
判断 A 的左子树与 B 的右子树是否对称
方法一:递归

/**
 * 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:
    bool helper(TreeNode* left,TreeNode* right){
     
        if(left==NULL&&right==NULL) return true;// 先写递归终止条件
        if(left==NULL&&right) return false;// 如果其中之一为空,也不是对称的
        if(left&&right==NULL) return false;
        if(left->val!=right->val) return false;
        return helper(left->left,right->right)&&helper(left->right,right->left);// 前序遍历
    }
    bool isSymmetric(TreeNode* root) {
     
        bool res=true;
        if(root==NULL) return true;
        if(root) res=helper(root->left,root->right);        
        return res;
    }
};

剑指Offer(二十七):二叉树的镜像(重要)

面试题27. 二叉树的镜像
面试题27. 二叉树的镜像
难度简单25
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/
2 7
/ \ /
1 3 6 9
镜像输出:
4
/
7 2
/ \ /
9 6 3 1
示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
限制:
0 <= 节点个数 <= 1000

/**
 * 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* mirrorTree(TreeNode* root) {
     
        if(root==NULL) return NULL;
        TreeNode* res=new TreeNode(root->val);
        res->left=mirrorTree(root->right);
        res->right=mirrorTree(root->left);
        return res;

    }
};

剑指Offer(二十六):树的子结构(重要)

面试题26. 树的子结构
难度中等55
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
例如:
给定的树 A:
3
/
4 5
/
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
示例 1:
输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:
输入:A = [3,4,5,1,2], B = [4,1]
输出:true
限制:
0 <= 节点个数 <= 10000
思路:首先现在A中找出B的根节点,也就是要遍历A找出与B根节点相同的值,然后判断树A中以R为根节点的子树是否包含和B一样的结构。 根节点值相等就继续判断剩余子树是否相等,直到B为NULL。

/**
 * 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:
    bool helper(TreeNode* A,TreeNode*B){
     
        if(B==NULL) return true;//B已经遍历完了都能对应的上
        if(A==NULL) return false;//A完B未完
        if(A->val!=B->val) return false;//如果其中有一个点没有对应上,返回false
        return helper(A->left,B->left)&&helper(A->right,B->right);//如果根节点对应的上,那么就分别去子节点里面匹配
    }
    bool isSubStructure(TreeNode* A, TreeNode* B) {
     
        bool res=false;
        if(A&&B){
     //当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
            if(A->val==B->val)res= helper(A,B);//如果找到了对应B的根节点的点,
            if(!res) res=isSubStructure(A->left,B);//如果找不到,那么就再去A的左子树当作起点
            if(!res) res=isSubStructure(A->right,B);//如果找不到,那么就再去A的右子树当作起点
        }
        return res;

    }
};

剑指Offer(七):重建二叉树(重要)

面试题07. 重建二叉树
难度中等108收藏分享切换为英文关注反馈
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7

限制:
0 <= 节点个数 <= 5000
思路分析:利用二叉树各种遍历的特点,找到根节点和左右子树 ,递归重建。注意前序遍历中的第一个数字是根节点的值,在中序遍历中根节点的值在序列中间,左子树的节点的值在根节点的值的左边,而右子树的节点的值位于根节点的右边,所以先扫描中序遍历,找到根节点所在位置,然后找到左子树和右子树的前序遍历和中序遍历即可。
方法一:递归一

/**
 * 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* buildTree(vector<int>& preorder, vector<int>& inorder) {
     
        if(preorder.empty()||inorder.empty()) return NULL;
        int val=preorder[0];// 前序遍历的第一个数字是根节点的值
        TreeNode* root=new TreeNode(val);// 创建根节点
        if(preorder.size()==1) return root;// 如果长度为1,直接返回根节点
        int rootIndex=0;// 找到root所在的位置,确定好前序和中序中左子树和右子树序列的范围
        for(int i=0;i<inorder.size();++i){
     
            if(inorder[i]==val){
     
                rootIndex=i;
                break;
            }
        }
        vector<int> leftPre,leftIn,rightPre,rightIn;
        for(int i=0;i<rootIndex;++i){
     
            leftPre.push_back(preorder[i+1]);// +1 是因为前序遍历的第一个节点是根节点
            leftIn.push_back(inorder[i]);
        }
        for(int i=rootIndex+1;i<preorder.size();++i){
     
            rightPre.push_back(preorder[i]);
            rightIn.push_back(inorder[i]);

        }

        root->left=buildTree(leftPre,leftIn);
        root->right=buildTree(rightPre,rightIn);
        return root;


    }
};

递归二:

class Solution {
     
public:
TreeNode *helper(vector<int> preorder,int preStart,int preEnd,vector<int> inorder,int inStart,int inEnd){
       
    if(preStart>=preEnd||inStart>=inEnd) return NULL;/
    TreeNode* root=new TreeNode(preorder[preStart]);
    int rootIndex=0;
    while(inorder[rootIndex]!=preorder[preStart]){
     
        rootIndex++;
    }
    cout<<rootIndex<<endl;
    root->left=helper(preorder,preStart+1,preStart+1+rootIndex-inStart,inorder,inStart,rootIndex);
    root->right=helper(preorder,preStart+1+rootIndex-inStart,preEnd,inorder,rootIndex+1,inEnd);    
    return root;
}
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
     
        if(preorder.size()<=0) return NULL;
    //前序遍历的第一个值是根节点的值,对应中序遍历同一个值
    TreeNode *root=new TreeNode(preorder[0]);
    return helper(preorder,0,preorder.size(),inorder,0,inorder.size());
    }
};

极简递归:

class Solution {
     
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
     
       //
       return recursionBuild(preorder.begin(),preorder.end(),inorder.begin(),inorder.end());

    }
    TreeNode* recursionBuild(vector<int>::iterator preBegin,vector<int>::iterator preEnd,vector<int>::iterator inBegin,vector<int>::iterator inEnd)
    {
     
        if(inEnd==inBegin) return NULL;
        TreeNode* cur=new TreeNode(*preBegin);
        auto root=find(inBegin,inEnd,*preBegin);
        cur->left=recursionBuild(preBegin+1,preBegin+(root-inBegin)+1,inBegin,root);
        cur->right=recursionBuild(preBegin+1+(root-inBegin),preEnd,root+1,inEnd);
        return cur;
    }
};

剑指Offer八:二叉树的下一个节点(重要)

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路分析:

我们以上图为例进行讲解,上图二叉树的中序遍历是d,b,h,e,i,a,f,c,g。我们以这棵树为例来分析如何找出二叉树的下一个结点。
如果一个结点有右子树,那么它的下一个结点就是它的右子树的最左子结点。也就是说从右子结点出发一直沿着指向左子树结点的指针,我们就能找到它的下一个结点。例如,图中结点b的下一个结点是h,结点a的下一个结点是f。
接着我们分析一下结点没有右子树的情形。如果结点是它父结点的左子结点,那么它的下一个结点就是它的父结点。例如,途中结点d的下一个结点是b,f的下一个结点是c。
如果一个结点既没有右子树,并且它还是父结点的右子结点,这种情形就比较复杂。我们可以沿着指向父结点的指针一直向上遍历,直到找到一个是它父结点的左子结点的结点。如果这样的结点存在,那么这个结点的父结点就是我们要找的下一个结点。例如,为了找到结点g的下一个结点,我们沿着指向父结点的指针向上遍历,先到达结点c。由于结点c是父结点a的右结点,我们继续向上遍历到达结点a。由于结点a是树的根结点。它没有父结点。因此结点g没有下一个结点。

/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
class Solution {
     
public:
    TreeLinkNode* GetNext(TreeLinkNode* Node)
    {
     
        if (Node== NULL)
            return NULL;
        TreeLinkNode* res = NULL;
        // 当前结点有右子树,那么它的下一个结点就是它的右子树中最左子结点
        if (Node->right != NULL)
        {
     
            TreeLinkNode* pRight = Node->right;
            while(pRight->left!=NULL)
            {
     
                pRight = pRight->left;
            }
            res = pRight;
        }
        // 当前结点无右子树,则需要找到一个是它父结点的左子树结点的结点
        else if (Node->next!=NULL)
        {
     
            // 当前结点
            TreeLinkNode* pCur = Node;
            // 父节点
            TreeLinkNode* pNext = Node->next;
            while( pNext != NULL && pNext->right == pCur)
            {
     
                pCur = pNext;
                pNext = pNext->next;
            }
            res = pNext;
        }
        return res;
    }
};

你可能感兴趣的:(剑指offer-二叉树,LeetCode刷题记录)