面试算法——递归解读

方法论:

  1. 看题五分钟,不会做,看解析;
  2. 先看中文站,再看国际站;
  3. 选择最优解析;
  4. 回头再来写

面试四步走:

  1. 和面试官,探讨题目限制条件;
  2. 说说可能解,选择最优解;
  3. 码字;
  4. 跑测试用例

背景

思维要点:

  1. 不要人肉进行递归
  2. 找到最近最简方法,将其拆解成可重复解决的问题(重复子问题)
  3. 数学归纳法思维

找到一种写代码的感觉!!!

void recur(int level, int param) {
    // terminator
    if (level > MAX_LEVEL) {
        // process result 
        return;
    }
    
    // process current logic
    process(level, param);
    
    // drill down
    recur(level: level + 1, newParam);
    
    // restore current status
}

C++种stringstream介绍,这里需要注意getline的用法;

// extract to string
#include <iostream>
#include <sstream>
#include <string>

int main ()
{
  std::string name;
  std::string name1;

  std::stringstream str("fan#zhi#hao#");
  std::getline (str, name, '#');
  std::cout << "Hello, " << name << "!\n";
  std::getline (str, name1, '#');
  std::cout << "Hello, " << name1 << "!\n";

  return 0;
}

104.二叉树的最大深度

可能解:

  1. 递归求解
  2. 迭代求解
# 1. 递归求解
class Solution {
public:
    int maxDepth(TreeNode* root) {
        // terminator
        if (root == nullptr)    return 0;
        //drill down
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};
# 2. 迭代求解
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr)    return 0;
        int res = 0;
        queue<TreeNode*> pq;
        pq.push(root);

        while (!pq.empty()) {
            int size = pq.size();
            for (int i = 0; i < size; i++) {
                TreeNode* tmp = pq.front();
                pq.pop();
                if (tmp->left)  pq.push(tmp->left);
                if (tmp->right) pq.push(tmp->right);
            }
            res++;
        }
        
        return res;
    }
};

111.二叉树的最小深度

可能解:

  1. 递归求解
# 1. 递归求解
class Solution {
public:
    int minDepth(TreeNode* root) {
        // terminator
        if (root ==nullptr) return 0;
        // dirll down
        int left = minDepth(root->left);
        int right = minDepth(root->right);
        // restore current status
        if (left == 0 || right == 0) return left+ right +1;
        return min(left, right) + 1;
    }
};

98.验证二叉搜索树

可能解:

  1. 定义函数help(bottom. root. upper)

  2. 利用二叉搜索树中序遍历有序性,验证二叉搜索树;

# 1T
class Solution {
public:
    bool isValidBST(TreeNode* root) {
        return helper(LONG_MIN, root, LONG_MAX);
    }

private:
    bool helper(long long bottom, TreeNode* root, long long upper) {
        if (root == nullptr)    return true;

        if (root->val <= bottom || root->val >= upper)  return false;
        
        return helper(root->val, root->right, upper) && helper(bottom, root->left, root->val);
    }
};

isValidBST(root)的定义为:如果以root为根的树为 BST, 那么改函数返回ture,否则返回false;以下为中序递归的模板!!!

# 2T
class Solution {
public:
    TreeNode* pre;
    bool isValidBST(TreeNode* root) {
        // terminator
        if (root == nullptr)    return true;
        
        // process current Logic
        // drill down
        if (!isValidBST(root->left)) {
            return false;
        }

        if (pre != nullptr && pre->val >= root->val){
            return false;
        }

        pre = root;

        if (!isValidBST(root->right)){
            return false;
        }
        
        // restore current status
        return true;
    }
};

226.翻转二叉树

以下会写三种方法,其中第一种、第二种类似(同为递归),第三种为迭代方法;迭代方法仍旧是利用stack模拟递归;

# 1.递归方法1
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr)    return nullptr;
        //if (root->right == nullptr && root->left == nullptr) return root;
        invertTree(root->left);
        invertTree(root->right);
        swap(root->left, root->right);
        return root;
    }
};
# 2.递归方法2
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        // terminator
        if (root == nullptr)    return nullptr;

        // process current logic
        TreeNode* tmp = root->left;
        root->left = invertTree(root->right);
        root->right = invertTree(tmp);

        // restore current status
        return root;

    }
};
# 3. 迭代方法
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*> sk;
        sk.push(root);

        while (!sk.empty()) {
            TreeNode* tmp = sk.top();
            sk.pop();

            if (tmp == nullptr) continue;
            sk.push(tmp->right);
            sk.push(tmp->left);

            swap(tmp->right, tmp->left);
        }
        return root;
    }
};

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

思路:利用前序遍历为:[preroot, tree(preroot->left), tree(preroot->left)],中序遍历为:[tree(inroot->left), inroot, tree(inroot->right)]的特性,设计递归函数mybuildtree(preorder, pre, end, iL),其中iL也就是中序遍历中root的下标。其中不同方法:主要体现在根据根据preroot->val找其在中序遍历的坐标不同上,以下代码是 利用map来记录坐标的不同 看代码:

class Solution {
public:
    unordered_map<int, int> umap;

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        for (int i = 0; i < inorder.size(); i++) {
            umap[inorder[i]] = i;
        }    
        return myBuildTree(preorder, 0, preorder.size()  - 1, 0);
    }

private: 
    TreeNode* myBuildTree(vector<int>& preorder, int pre, int end, int inL){
        if (pre > end)  return nullptr;

        int index = umap[preorder[pre]];
        int leftNum = index - inL;

        TreeNode* root = new TreeNode(preorder[pre]);
        root->left = myBuildTree(preorder, pre + 1, pre + leftNum, inL);
        root->right = myBuildTree(preorder, pre + leftNum + 1, end, index + 1);
        return root;
    }
};

236.二叉树的最近公共祖先

可能解:

  1. 递归求解的基本路子;
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        // terminator
        if (root == nullptr)    return nullptr; // 断臂操作
        if (root == p || root == q)  return root;
        
        // process current logic 
        // drill down
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right  = lowestCommonAncestor(root->right, p, q);
        
        // restore current status
        if (left == nullptr && right == nullptr)    return nullptr;
        if (left ==nullptr) return right;
        if (right == nullptr)   return left;
        return root;        
    }
};

22.括号生成

思路:由于这类题有无限解,一定是递归回溯方法。设计函数_generate(int left, int right, int n, string str),因此需要考虑以下几点:

  1. 终止条件terminator是:括号数量,left == right,则为符合条件的一个;
  2. 递归条件:left < n, drill down, _generate(left + 1, right, n, str + '(')
  3. 递归条件:left > right, drill down, _generate(left, right+1, n, str + ')')
class Solution {
public:

    vector<string> res;
    vector<string> generateParenthesis(int n) {
        _generate(0, 0, n, "");
        return res;
    }

private:
    void _generate(int left, int right, int n, string str) {
        if (left == n && right == n) {
            res.push_back(str);
        }

        if (left < n) _generate(left + 1, right, n, str + '(');
        if (left > right) _generate(left, right + 1, n, str + ')');
    }
};

参考

  1. 这个链接是关于stack和queue相关题目的集合——Basic C++ iterative solution with detailed explanations. Super easy for beginners.
  2. 极客时间-算法训练营-覃超
  3. leetcode中文站
  4. leetcode国际站 (将力扣中文链接,后面的-cn去掉,就是该题的国际站)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值