2020秋招算法题笔记-二叉树

144. 二叉树的前序遍历

复习思路链接
链接二叉树的前序遍历

题目:给定一个二叉树,返回它的 前序 遍历。
递归版本:
答案:

/**
 * 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> ans;
    void preorder(TreeNode* root) {
        if (root==NULL){ return ;}
        ans.push_back(root->val);
        preorder(root->left);
        preorder(root->right);
    }
    vector<int> preorderTraversal(TreeNode* root){
        preorder(root);
        return ans;
    }

};

注意:

return为空的时候函数也要记得用void
数组的插入ans.push_back(val)
弹出:ans.pop_back(val)
ans.clear() //清空所有元素
ans.erase() //删除指定元素
ans.is_empty() //判断是否为空

非递归方式:
添加链接描述

非递归的方式也很简单,无非就是压栈/弹栈的过程, 先序的话就是先压入根节点,然后while循环判断右不为空,就弹出当前并访问,依次再压入右子树和左子树,
注意三个知识点:
建立一个栈 stack< TreeNode> ans

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

注意:

思路,temp指针的作用,要么遍历左子树直到尽头,要么指向右子树
进入弹出规则:边访问(保存值)边压栈,左子树空则弹出当前节点,并把当前节点的右节点当作新的根节点赋值
while的条件一是因为后面要用temp=mystack.top() ,如果栈为空的话根本不可以用这句

中序遍历:
题目

/**
 * 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> inorderTraversal(TreeNode* root) {
        if (root==NULL) return {};
        vector <int> ans;
        stack <TreeNode *> mystack;
        TreeNode * temp =root;
        while(!mystack.empty()||temp){
            while(temp){
                mystack.push(temp);
                temp=temp->left;
            }
            TreeNode* node=mystack.top();
            ans.push_back(node->val);
            mystack.pop();
            temp=node->right;
        }
    return ans;
    }
};

注意点:

每次mystack.push的时候请push temp本身,因为temp的作用就是指向当前节点,temp想要指向右节点的时候,注意用刚出栈的node的右节点而非原始temp。
思路:和前序遍历差不多,不同的是前序遍历是压栈前访问,而在中序是出栈前访问

后序:

/**
 * 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> postorderTraversal(TreeNode* root) {
        if (root==NULL) return {};
        vector <int> ans;
        stack <TreeNode *> mystack;
        TreeNode* temp=root;
        while(!mystack.empty()||temp){
            while(temp){
                ans.push_back(temp->val);
                mystack.push(temp);
                temp=temp->right;
            }
            TreeNode * node=mystack.top();
            mystack.pop();
            temp=node->left;
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

注意:

二叉树最大深度

思路:深度优先的方法:递归方法,过于简单,遍历左子树,遍历右子树,递归出口为(null 时return 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:

    int preorder(TreeNode* root){
        if (root ==NULL){return 0;}
        int ldepth=1+preorder(root->left);
        int rdepth=1+preorder(root->right);
        return max(ldepth,rdepth);
    }
    int maxDepth(TreeNode* root) {
        if (root ==NULL){
            return 0;
        }
        if (root->left==NULL && root->right==NULL){return 1;}
        int maxheight=0;
        maxheight=preorder(root);
        return maxheight;
    }
};

广度优先的方法:广度优先是要维护一个队列,正常来说,队列取了myqueue.front(),后会把它所有子节点都送入,但这样的话队列中就一直有父节点和子节点的混合,所有用一个变量sz控制队列上一行的内容个数,处理完这些个数后,队列中剩下的元素就是下曾队列的了。
题解

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        queue<TreeNode*> Q;
        Q.push(root);
        int ans = 0;
        while (!Q.empty()) {
            int sz = Q.size();
            while (sz > 0) {
                TreeNode* node = Q.front();Q.pop();
                if (node->left) Q.push(node->left);
                if (node->right) Q.push(node->right);
                sz -= 1;
            }
            ans += 1;
        } 
        return ans;
    }
};

二叉平衡树的判断

题目:
110. 平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
思路:

必须要保证左子树和右子树都是平衡二叉树且当前的树高相差不大于1.此方法为暴力法,判断平衡树的函数就要遍历每个节点。且,每次遍历到一个点就要去查它的高度,所以时间复杂度为 O(Nlog_2 N)

官方复杂度分析:
时间复杂度 O(Nlog_2 N), 最差情况下, isBalanced(root) 遍历树所有节点,占用 O(N);判断每个节点的最大高度 depth(root) 需要遍历 各子树的所有节点 ,子树的节点数的复杂度为 O(log_2 N)

空间复杂度 O(N): 最差情况下(树退化为链表时),系统递归需要使用 O(N)的栈空间。
题解:https://leetcode-cn.com/problems/balanced-binary-tree/solution/balanced-binary-tree-di-gui-fang-fa-by-jin40789108/
代码:

/**
 * 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 depth(TreeNode * root){
        if (root==NULL) return 0;
        int ldepth=1+depth(root->left);
        int rdepth=1+depth(root->right);
        return (max(ldepth,rdepth));
    }

    bool isBalanced(TreeNode* root) {
        if (root==NULL){
            return true;
        }
        return ((abs(depth(root->left)-depth(root->right))<=1)&&isBalanced(root->right)&&isBalanced(root->left));
    }    
};

解二:自低向上,提前阻断,遍历过程中即可不断返回-1,提前阻断遍历。在递归过程中中,就像是要计算depth一样,但又有点区别,在recur函数中返回的值有两种含义,一是树高,二是不断在递归过程中去看子树是否为平衡树,所以不满足时返回的是-1,满足平衡树时返回的是树高. 时间O(N) ;空间O(N)

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

//from bottom to top
class Solution {
public:
    int recur(TreeNode * root){
        if (root==NULL) return 0;
        int ldepth=recur(root->left);
        if (ldepth==-1) return -1;
        int rdepth=recur(root->right);
        if (rdepth==-1) return -1;
        return (abs(rdepth-ldepth)<2?max(rdepth,ldepth)+1:-1);
    }
    bool isBalanced(TreeNode* root){
        // if (root==NULL){
        //     return true;
        // }
        return !(recur(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:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        if (!root) return {};
        queue <TreeNode*> myqueue;
        vector<vector<int>> myans; 
        vector<int> temp;
        myqueue.push(root);
        TreeNode* p=root;
        int count=0;
        while(!myqueue.empty()){
            count+=1;
            int mysize=myqueue.size();
            while(mysize!=0){
                p=myqueue.front(); myqueue.pop();
                temp.push_back(p->val);
                mysize-=1;
                if (p->left) {myqueue.push(p->left);}
                if (p->right) { myqueue.push(p->right);}
            }
        if (count%2==0)
            {reverse(temp.begin(),temp.end());}
        myans.push_back(temp);
        temp={};

        }
    return myans;
    }
};

<>

105. 从前序与中序遍历序列构造二叉树

https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树:

 3
/  \
9  20
   /  \
  15   7

思想:

方法一:递归 思路
对于任意一颗树而言,前序遍历的形式总是
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ] 即根节点总是前序遍历中的第一个节点。而中序遍历的形式总是
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]
只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。

这样以来,我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置。

细节

在中序遍历中对根节点进行定位时,一种简单的方法是直接扫描整个中序遍历的结果并找出根节点,但这样做的时间复杂度较高。我们可以考虑使用哈希映射(HashMap)来帮助我们快速地定位根节点。对于哈希映射中的每个键值对,键表示一个元素(节点的值),值表示其在中序遍历中的出现位置。在构造二叉树的过程之前,我们可以对中序遍历的列表进行一遍扫描,就可以构造出这个哈希映射。在此后构造二叉树的过程中,我们就只需要
O(1)O(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 {
private:
    unordered_map<int, int> index;

public:
    TreeNode* myBuildTree(const vector<int>& preorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return nullptr;
        }
        
        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = index[preorder[preorder_root]];
        
        // 先把根节点建立出来
        TreeNode* root = new TreeNode(preorder[preorder_root]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root->left = myBuildTree(preorder,  preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root->right = myBuildTree(preorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        // 构造哈希映射,帮助我们快速定位根节点
        for (int i = 0; i < n; ++i) {
            index[inorder[i]] = i;
        }
        return myBuildTree(preorder, 0, n - 1, 0, n - 1);
    }
};



二叉搜索树

剑指 Offer 54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第k大的节点。
示例 1:
在这里插入图片描述
本文解法基于此性质:二叉搜索树的中序遍历为 递增序列 。
根据以上性质,易得二叉搜索树的 中序遍历倒序 为 递减序列 。
因此,求 “二叉搜索树第 kk 大的节点” 可转化为求 “此树的中序遍历倒序的第 kk 个节点”。

/**
 * 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 kth = 0;
    int res;;
    int kthLargest(TreeNode* root, int k) {
        kth = k;
        solver(root);
        return res;
    }

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值