• 二叉树


    1 二叉树与分治递归

    几乎所有的二叉树问题都可以通过分治解决,包括二叉树的遍历、二叉树中求最大分支长度、最大深度任意两个结点间的最大距离,搜索二叉树,平衡二叉树 等等,只要题目中没有严格的时间性能的要求,使用递归的方法时都可以优先考虑分治递归的方法,除了二叉树的层序遍历以外。但然很多题目虽然也可以用变通递归来实现,但解题过程更难想到。

    1.1 分治递归与普通递归的模板

    Template 1: Traverse
    
    public class Solution {
        public void traverse(TreeNode root) {
            if (root == null) {
                return;
            }
            // do something with root
            traverse(root.left);
            // do something with root
            traverse(root.right);
            // do something with root
        }
    }
    
    
    Tempate 2: Divide & Conquer
    
    public class Solution {
        public ResultType traversal(TreeNode root) {
            // null or leaf
            if (root == null) {
                // do something and return;
            }
            
            // Divide
            ResultType left = traversal(root.left);
            ResultType right = traversal(root.right);
            
            // Conquer
            ResultType result = Merge from left and right.
            return result;
        }
    }
    

    1.2 经典的二叉树前中后遍历也可以用分治递归方法

        /*method2 division and conquer*/
        vector<int> preorderTraversal(TreeNode *root) {
            vector<int> result;
            if (root == NULL) {
                return result;
            }
            //division
            vector<int> left, right;
            left = preorderTraversal(root->left);
            right = preorderTraversal(root->right);
            //conquer
            result.push_back(root->val);
            result.insert(result.end(), left.begin(), left.end());
            result.insert(result.end(), right.begin(), right.end());
            
            return result;
        }
    

    2 二叉树问题用非递归方法解决

    二叉树问题用非递归方法解决一般是题目要求时间有限,不能使用递归方式,或者直接要求使用非递归方法。

    2.1 二叉树前序遍历非递归

    void preorder(TreeNode *root) {
        if (root == NULL) {
            return;
        }
        stack<TreeNode*> stk;
        stk.push(root);
        while (!stk.empty()) {
            TreeNode *curt = stk.top();
            cout<<curt->val<<" ";
            stk.pop();
            if (curt->right != NULL) {
                stk.push(curt->right);
            }
            if (curt->left != NULL) {
                stk.push(curt->left);
            }
        }
    }
    

    2.2 二叉树中序遍历非递归

    二叉树中序遍历的非递归考的比较少,后序遍历的非递归考得就更少了。中序遍历的非递归除了使用栈以外,还需要维护一个当前指针root,具体思路如下:

    1. 首先使用root找到左下方第一个没有左子树的结点并且入栈,并把沿途结点也全部入栈;
    2. 访问当前栈顶元素,并把对当前结点的右子树进行步骤 1);
    3. 如果栈空结束结束该过程;
    4. 因为刚开始进行循环与最终结束循环时,栈都为空,为了使用开始状态时,能够进入循环,需要将循环判断改为(!stk.empty() || root != NULL),使用root != NULL进入循环;
    void inorder(TreeNode *root) {
        if (root == NULL) {
            return;
        }
        stack<TreeNode*> stk;
        while (!stk.empty() || root != NULL) {
            while (root != NULL) {
                stk.push(root);
                root = root->left;
            }
            root = stk.top();
            cout<<root->val<<" ";
            stk.pop();
            root = root->right;
        }
    }
    

    2.3 二叉树的层序遍历

    使用队列遍历,注意应该先将右子树放入,再将左子树放入,这样才能每层从左向右访问。

    void levelorder(TreeNode *root) {
        if (root == NULL) {
            return NULL;
        } 
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            TreeNode *node = que.front();
            que.pop();
            cout<<node->val<<" ";
            if (node->left != NULL) {
                que.push(node->left);
            }
            if (node->right != NULL) {
                que.push(node->right);
            }
        }
    }
    

    2.4 图的层序遍历

    说到二叉树层序遍历,那自然也不得不讨论一下逻辑完全相同的图的BFS,图的大部分问题都需要遍历来解决,而且优先也考虑使用BFS而非DFS,因为BFS一般找到访问过的点的情况要比DFS少。

    void searchNode(vector<UndirectedGraphNode*>& graph, map<UndirectedGraphNode*, int>& values, UndirectedGraphNode* node, int target) {
        if (node == NULL) {
            return ;
        } 
        queue<UndirectedGraphNode*> que;
        que.push(node);
        unordered_set<UndirectedGraphNode*> hash;
        hash.insert(node);
        while(!que.empty()) {
            UndirectedGraphNode *cur = que.front();
            que.pop();
            cout<<cur->val; 
            for (int i = 0; i < cur->neighbors.size(); i++) {
                if (hash.find(cur->neighbors[i]) == hash.end()) {
                    hash.insert(cur);
                    que.push(cur->neighbors[i]);
                }
           }
        }
    }
    
    

    从上面的代码可以看出,图的BFS遍历与二叉树的遍历过程相比:

    1. 需要维护一个hash表来判断该邻居是否遍历过,只有没有遍历过的邻居才会加入队列进行之后的访问。
    2. 因为二叉树只有两个孩子,但是图的每个结点的邻居个数不确定,因此需要循环判断是否要将图的邻居是否入队。

    3 本章遇到的值得回味题目

    Lowest Common Ancestor
    Binary Tree Maximum Path Sum
    Validate Binary Search Tree
    本题技巧:

    1. 为了简化代码,空节点可以初始化为最大值,与最小值 来方便后续的比较;
    2. 本题给出的测试样本中有些就是INT_MAX或者INT_MIN,因此我们空节点初始化时应该初始为LONG_MAX与LONG_MIN;
  • 相关阅读:
    P1351 联合权值
    c++ 贪心讲解大礼包
    取石子 找规律
    树 dfs暴力判环 题意转化
    P2519 [HAOI2011]problem a
    P1640 [SCOI2010]连续攻击游戏 二分图最大匹配 匈牙利算法
    P2756 飞行员配对方案问题 二分图匹配 匈牙利算法
    cogs 49. 跳马问题 DFS dp
    cogs 2. 旅行计划 dijkstra+打印路径小技巧
    cogs 1440. [NOIP2013]积木大赛 贪心水题
  • 原文地址:https://www.cnblogs.com/fariver/p/7154065.html
Copyright © 2020-2023  润新知