[LEECODE]算法进阶自练习5-6 递归&哈希表

232 阅读5分钟

[LEECODE]算法进阶自练习5-6 递归&哈希表

5.递归

简单
  1. 二叉树的最大深度 leetcode 104  递归解法(简单易理解):
    int maxDepth(TreeNode* root) {
        if(root ==  nullptr) return 0;
        int leftDepth = maxDepth(root->left);
        int rightDepth = maxDepth(root->right);
        return max(leftDepth, rightDepth) + 1;
    }

 广度优先搜索(层序遍历):

    int maxDepth(TreeNode* root) {
        int ans = 0;
        if(!root) return ans;
        queue<TreeNode*> queue;
        queue.push(root);
        while(!queue.empty()){
            int size = queue.size();
            while(size > 0){
                TreeNode* node = queue.front(); queue.pop();
                if(node->left) queue.push(node->left);
                if(node->right) queue.push(node->right);
                size--;
            }
            ans++;
        }
        return ans;
    }
  1. 对称二叉树 leetcode 101  递归解法:
    bool check(TreeNode* l, TreeNode* r){
        if(!l && !r) return true; // 左右节点都为空了
        if(!l || !r) return false; // 有一个节点不为空
        return l->val == r->val && check(l->left,r->right) && check(l->right,r->left);
    }
    bool isSymmetric(TreeNode* root) {
        if(!root) return true;
        return check(root->left, root->right);
    }

 层序遍历解法:

    bool check(TreeNode* l, TreeNode* r){
        queue<TreeNode*> queue;
        queue.push(l);queue.push(r);
        while(!queue.empty()){
            TreeNode* tl = queue.front(); queue.pop();
            TreeNode* tr = queue.front(); queue.pop();
            if(!tl && !tr) continue;
            if((!tl || !tr) || tl->val != tr->val) return false;
            queue.push(tl->left);
            queue.push(tr->right);
            queue.push(tl->right);
            queue.push(tr->left);
        }
        return true;
    }
    bool isSymmetric(TreeNode* root) {
        if(!root) return true;
        return check(root->left, root->right);
    }
  1. 二叉树的最小深度 leetcode 111  递归解法:
    int minDepth(TreeNode* root) {
        if(!root) return 0;
        if(root->left == nullptr && root->right == nullptr) return 1;
        int minDep = INT_MAX;
        if(root->left){
            minDep = min(minDep,minDepth(root->left));
        }
        if(root->right){
            minDep = min(minDep,minDepth(root->right));
        }
        return minDep + 1; // +1是加上当前节点
    }

 广度优先解法:

int minDepth(TreeNode* root) {
        if(!root) return 0;
        int ret = 0;
        queue<pair<TreeNode*, int>> queue;
        queue.emplace(root,1);
        while (!queue.empty()) {
            TreeNode *node = queue.front().first;
            int depth = queue.front().second;
            queue.pop();
            if (node->left == nullptr && node->right == nullptr) {
                return depth;
            }
            if (node->left != nullptr) {
                queue.emplace(node->left, depth + 1);
            }
            if (node->right != nullptr) {
                queue.emplace(node->right, depth + 1);
            }
        }
        return 0;
    }
  1. 二叉搜索树节点最小距离 leetcode 783  注意此题是求任意两个节点差值 递归解法:
class Solution {
public:
    vector<int> nums;
    void collection(TreeNode* root){
        if(!root) return;
        if(root->left) collection(root->left);
        nums.push_back(root->val);
        if(root->right) collection(root->right);
    }
    int minDiffInBST(TreeNode* root) {
        int min = INT_MAX;
        collection(root);
        for(int i = 1; i< nums.size(); i++){
            min = min > abs(nums[i] - nums[i-1]) ? abs(nums[i] - nums[i-1]) : min;
        }
        return min;
    }
};

 深度优先遍历解法:

class Solution {
public:
    vector<int> nums;
    void collection(TreeNode* root){
        if(!root) return;
        stack<TreeNode*> st;
        while(root || !st.empty()){
            while(root){
                st.push(root);
                root = root->left;
            }
            if(!st.empty()){
                root = st.top();
                st.pop();
                nums.push_back(root->val);
                root = root->right;
                
            }   
        }
    }
    int minDiffInBST(TreeNode* root) {
        int min = INT_MAX;
        collection(root);
        for(int i = 1; i< nums.size(); i++){   
            min = min > abs(nums[i] - nums[i-1]) ? abs(nums[i] - nums[i-1]) : min;
        }
        return min;
    }
};
  1. 二叉搜索树的范围和 leetcode 938  这个题目很简单,但是题目没有说明白,乍一看很懵,看了题解才知道这样啊。如果根节点值>=L&& <=R 计算左右子树和,如果根节点值<L计算右子树,如果根节点值>R计算左子树即可,这样递归就很简单了。
class Solution {
public:
    int num = 0;
    void rangeSum(TreeNode* root, int L, int R){
        if(!root) return;
        if(root->val >= L && root->val <= R){
            num += root->val;
            rangeSum(root->right,L,R);
            rangeSum(root->left,L,R);
        }
        if(root->val > R){
            rangeSum(root->left,L,R);
        }
        if(root->val < L){
            rangeSum(root->right,L,R);
        }
    }
    int rangeSumBST(TreeNode* root, int L, int R) {
        rangeSum(root, L, R);
        return num;
    }
};

 非递归版本就更容易了,直接全部遍历然后计算和就行了,这里其实应该可以优化如果在L和R之间的去遍历就可以了,其他的就没必要处理了。

class Solution {
public:
    int num = 0;
    void rangeSum(TreeNode* root, int L, int R){
        if(!root) return;
        stack<TreeNode*> st;
        while(!st.empty() || root){
            while(root){
                st.push(root);
                root = root->left;
            }
            if(!st.empty()){
                root = st.top();
                st.pop();
                if(root->val >= L && root->val <= R){
                    num += root->val;
                }
                root = root->right;
            }
        } 
    }
    int rangeSumBST(TreeNode* root, int L, int R) {
        rangeSum(root, L, R);
        return num;
    }
};
中等
  1. 二叉树的最近公共祖先 leetcode 236  递归解法,理解思路可以参考这里(leetcode-cn.com/problems/lo…
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        /*
            1.如果 p 和 q 都存在,则返回它们的公共祖先;
            2.如果只存在一个,则返回存在的一个;
            3. 如果 p 和 q 都不存在,则返回NULL
        */
        if(!root) return nullptr;
        if(root == p || root == q) return root;
        
        TreeNode* l = lowestCommonAncestor(root->left,p,q);
        TreeNode* r = lowestCommonAncestor(root->right,p,q);
        if(!l) return r; // p q在右子树  注意这里判断条件是先判断为空的, 不然走不到 l&&r
        if(!r) return l; // p q在左子树  注意这里判断条件是先判断为空的, 不然走不到 l&&r
        if(l && r) return root; // p q在左右两个子树
        return nullptr;
    }

 记录父节点,然后遍历查找最后的一个共同的父节点。

class Solution {
public:
    unordered_map<int, TreeNode*> fa;
    unordered_map<int, bool> vi;
    void dfs(TreeNode* root){
        if(root->left){
            fa[root->left->val] = root;
            dfs(root->left);
        }
        if(root->right){
            fa[root->right->val] = root;
            dfs(root->right);
        }
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        fa[root->val] = nullptr;
        dfs(root);
        while(p != nullptr){
            vi[p->val] = true;
            p = fa[p->val];
        }
        while(q != nullptr){
            if(vi[q->val]) return q;
            q = fa[q->val];
        }
        return nullptr;
    }
};

6.哈希表

简单
  1. 两数之和 leetcode 1  这里就用哈希表来解决就行了,还有几个解法,我之前在公众号有写过。
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> mp;
        vector<int> ret;
        int nsize = nums.size();
        for(int i=0; i<nsize; i++){
            mp[nums[i]] = i;
        }
        for(int i=0; i<nsize; i++){
            if(mp.count(target-nums[i]) != 0 && i != mp[target-nums[i]]){
                ret.push_back(i);
                ret.push_back(mp[target-nums[i]]); // 找到一对返回就行了
                return ret;
            }
        }
        return ret;
    }
  1. 有效的字母异位词 leetcode 242  这个也比较简单,第一个字符串每个字符放到map里面++ 第二个字符串每个字符放到map里面--,如果为0了erase掉就行了。
    bool isAnagram(string s, string t) {
        int ssize = s.size(), tsize = t.size();
        if(s.size() != t.size()) return false;
        map<char,int> mp;
        for(int i=0; i<ssize; i++){
            if(mp.count(s[i]) > 0){
                mp[s[i]]++;
            }else{
                mp[s[i]] = 1;
            }
        }
        for(int i=0; i<tsize; i++){
            if(mp.count(t[i]) > 0){
                mp[t[i]]--;
                if(mp[t[i]] == 0){
                    mp.erase(t[i]);
                }
            }
            else{
                return false;
            }
        }
        return mp.empty();
    }
中等
  1. 无重复字符的最长子串 leetcode 3  双指针,一个指向最长子串的左侧,一个指向最长子串的右侧,如果出现了重复的子串,就将左指针指向出现的下一个位置
    int lengthOfLongestSubstring(string s) {
        map<char, int> mp; // map<char, idx+1>
        int ssize = s.size();
        int start = 0;
        int ret = 0;
        for(int i=0; i<ssize; i++){
                if(mp.count(s[i]) == 0 || mp[s[i]] < start){ // 没出现过 计算长度 注意这里判断 mp[s[i]] < start
                ret = max(ret, i-start+1);
            }
            else{
                start = mp[s[i]]; // 出现过了设置start位置
            }
            mp[s[i]] = i+1;
        }
        return ret;
    }
  1. 前K个高频单词 leetcode 692  先统计单词出现的频率,然后用优先队列做最小堆
typedef pair<int, string> PairM;
struct GreaterM{
    bool operator()(const PairM& a, const PairM& b){
        return (a.first > b.first || (a.first == b.first && a.second < b.second));
    }
};
class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        int wsize = words.size();
        map<string, int> mp;
        for(int i=0; i<wsize; i++){
            mp[words[i]]++;
        }
        priority_queue<PairM,vector<PairM>,GreaterM> queue;
        for(auto &item:mp){
            PairM tmp{item.second, item.first};
            if(queue.size() < k){
                queue.push(tmp);
            }
            else{
                queue.push(tmp);
                queue.pop();
            }
        }

        vector<string> ret;
        while(!queue.empty()){
            ret.push_back(queue.top().second);
            queue.pop();
        }
        reverse(ret.begin(),ret.end());
        return ret;

    }
};
困难
  1. 原子的数量 leetcode 726
class Solution {
public:
    int index;
    bool isUpperCase(const char& c){
        return c >= 'A' && c <= 'Z';
    }
    bool isLowerCase(const char& c){
        return c >= 'a' && c <= 'z';
    }
    bool isNumber(const char& c){
        return c >= '0' && c <= '9';
    }
    string countOfAtoms(string formula) {
        auto atomCounter = parse(formula);
        string res = "";
        for(auto [k,v] : atomCounter){
            res += k;
            if(v == 1) continue;
            res += to_string(v);
        }
        return res;
    }
    map<string, int> parse(string formula){
        int flen = formula.length();
        map<string, int> ret;
        while(index < flen && formula[index] != ')'){
            if(formula[index] == '('){
                index++;
                for(auto [k,v] : parse(formula)){
                    ret[k] += v;
                }
            }
            else{
                int start = index;
                index++;
                while(isLowerCase(formula[index])) index++;
                string atomName = formula.substr(start, index-start);
                start = index;
                while(isNumber(formula[index])) index++;
                int num = 1;
                if(index > start) num = stoi(formula.substr(start, index-start));
                ret[atomName] += num;

            }
        }
        index++;
        if(index < flen){
            int start = index;
            while(isNumber(formula[index])) index++;
            int multiFactor = 1;
            if(index > start) multiFactor = stoi(formula.substr(start, index-start));
            for(auto [k,v]: ret) ret[k] *= multiFactor;
        }
        return ret;
    }
};