leetcode解题思路分析(七十七)677 - 683 题

  1. 键值映射
    实现一个 MapSum 类,支持两个方法,insert 和 sum

标准trie树

struct Trie {
    
    
    Trie* next[26];
    bool isend;
    int val;

    Trie() {
    
    
        for (int i = 0; i < 26; i++) {
    
    
            next[i] = nullptr;
        }
        isend = false;
    }
};

class MapSum {
    
    
public:
    Trie root;
    /** Initialize your data structure here. */
    MapSum() {
    
    
        // do nothing
    }
    
    void insert(string key, int val) {
    
    
        Trie *cur = &root;

        for (int i = 0; i < key.size(); i++) {
    
    
            char c = key[i];
            if (cur->next[c - 'a'] == nullptr) {
    
    
                cur->next[c - 'a'] = new Trie();
            }
            cur = cur->next[c - 'a'];
        }
        cur->isend = true;
        cur->val = val;
    }
    
    void dfs(Trie *root, int& ans) {
    
    
        if (root->isend) {
    
    
            ans += root->val;
        }
        for (int i = 0; i < 26; i++) {
    
    
            if (root->next[i] != nullptr) {
    
    
                dfs(root->next[i], ans);
            }
        }
    }

    int sum(string prefix) {
    
    
        Trie *cur = &root;
        int ans = 0;

        for (int i = 0; i < prefix.size(); i++) {
    
    
            char c = prefix[i];
            if (cur->next[c - 'a'] == nullptr) {
    
    
                return ans;
            }
            cur = cur->next[c - 'a'];
        }
        dfs(cur, ans);
        return ans;
    }
};

/**
 * Your MapSum object will be instantiated and called as such:
 * MapSum* obj = new MapSum();
 * obj->insert(key,val);
 * int param_2 = obj->sum(prefix);
 */

  1. 有效的括号字符串
    给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。

nMin、nMax表示「可能多余的左括号」,一个下界,一个上界,很直观。执行起来就是
遇到左括号:nMin++, nMax++
遇到星号:nMin–, nMax++(因为星号有三种情况)
遇到右括号:nMin–, nMax–
其中nMin要保持不小于0。
如果nMax < 0,说明把星号全变成左括号也不够了,False
最後,如果nMin != 0,说明末尾有多余的左括号,False

class Solution {
    
    
public:
    bool checkValidString(string s) 
    {
    
    
        int nMax = 0, nMin = 0;

        for (auto c : s)
        {
    
    
            switch (c)
            {
    
    
            case '(':
                nMax++;
                nMin++;
                break;

            case ')': 
                nMax--;
                if (nMin > 0) nMin--;
                break;

            case '*': 
                if (nMin > 0) nMin--;
                nMax++;
                break;
            }

            if (nMax < 0) return false;
        }

        return nMin == 0;
    }
};
  1. 24点游戏
    你有 4 张写有 1 到 9 数字的牌。你需要判断是否能通过 *,/,+,-,(,) 的运算得到 24。

回溯穷举各种组合即可

class Solution {
    
    
public:
    static constexpr int TARGET = 24;
    static constexpr double EPSILON = 1e-6;
    static constexpr int ADD = 0, MULTIPLY = 1, SUBTRACT = 2, DIVIDE = 3;

    bool judgePoint24(vector<int> &nums) {
    
    
        vector<double> l;
        for (const int &num : nums) {
    
    
            l.emplace_back(static_cast<double>(num));
        }
        return solve(l);
    }

    bool solve(vector<double> &l) {
    
    
        if (l.size() == 0) {
    
    
            return false;
        }
        if (l.size() == 1) {
    
    
            return fabs(l[0] - TARGET) < EPSILON;
        }
        int size = l.size();
        for (int i = 0; i < size; i++) {
    
    
            for (int j = 0; j < size; j++) {
    
    
                if (i != j) {
    
    
                    vector<double> list2 = vector<double>();
                    for (int k = 0; k < size; k++) {
    
    
                        if (k != i && k != j) {
    
    
                            list2.emplace_back(l[k]);
                        }
                    }
                    for (int k = 0; k < 4; k++) {
    
    
                        if (k < 2 && i > j) {
    
    
                            continue;
                        }
                        if (k == ADD) {
    
    
                            list2.emplace_back(l[i] + l[j]);
                        } else if (k == MULTIPLY) {
    
    
                            list2.emplace_back(l[i] * l[j]);
                        } else if (k == SUBTRACT) {
    
    
                            list2.emplace_back(l[i] - l[j]);
                        } else if (k == DIVIDE) {
    
    
                            if (fabs(l[j]) < EPSILON) {
    
    
                                continue;
                            }
                            list2.emplace_back(l[i] / l[j]);
                        }
                        if (solve(list2)) {
    
    
                            return true;
                        }
                        list2.pop_back();
                    }
                }
            }
        }
        return false;
    }
};
  1. 验证回文字符串2
    给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
    采用双指针滑动+贪心算法,当遇到首尾不一样的时候,检测是否能通过删除其中一个来得到回文串,如果可以则true,否则为false
class Solution {
    
    
public:
    bool checkPalindrome(const string& s, int low, int high) 
    {
    
    
        while (low < high)
        {
    
    
            if (s[low] != s[high]) return false;
            low++;
            high--;
        }
        return true;
    }

    bool validPalindrome(string s) 
    {
    
    
        int low = 0, high = s.size() - 1;
        while (low < high) 
        {
    
    
            char c1 = s[low], c2 = s[high];
            if (c1 == c2) 
            {
    
    
                ++low;
                --high;
            } 
            else 
            {
    
    
                return checkPalindrome(s, low, high - 1) || checkPalindrome(s, low + 1, high);
            }
        }
        return true;
    }
};


  1. 棒球比赛
    你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。

用栈即可


class Solution {
    
    
public:
    int calPoints(vector<string>& ops) {
    
    
        stack<int> nums;
        for (string& s : ops)
        {
    
    
            // cout << s << " " << nums.size() << endl;
            if (s == "+")
            {
    
    
                // 这里要记得把弹出来的数字先插回去
                int n1 = nums.top();
                nums.pop();
                int n2 = nums.top();
                nums.push(n1);
                nums.push(n1+n2);
            }
            else if (s == "D")
            {
    
    
                nums.push(nums.top() * 2);
            }
            else if (s == "C")
            {
    
    
                nums.pop();
            }
            else
            {
    
    
                nums.push(stoi(s));
            }
        }

        // 累加和获得结果
        int res = 0;
        while (!nums.empty())
        {
    
    
            res += nums.top();
            nums.pop();
        }
        return res;
     }
};
  1. 冗余连接
    输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。

使用集合存储边,轮询检查即可

class Solution {
    
    
private:
    int n = 1005; // 节点数量3 到 1000
    int father[1005];

    // 并查集初始化
    void init() {
    
    
        for (int i = 0; i < n; ++i) {
    
    
            father[i] = i;
        }
    }
    // 并查集里寻根的过程
    int find(int u) {
    
    
        return u == father[u] ? u : father[u] = find(father[u]);
    }
    // 将v->u 这条边加入并查集
    void join(int u, int v) {
    
    
        u = find(u);
        v = find(v);
        if (u == v) return ;
        father[v] = u;
    }
    // 判断 u 和 v是否找到同一个根,本题用不上
    bool same(int u, int v) {
    
    
        u = find(u);
        v = find(v);
        return u == v;
    }
public:
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
    
    
        init();
        for (int i = 0; i < edges.size(); i++) {
    
    
            if (same(edges[i][0], edges[i][1])) return edges[i];
            else join(edges[i][0], edges[i][1]);
        }
        return {
    
    };
    }
};

  1. 冗余连接2
    在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。
    输入一个有向图,该图由一个有着 n 个节点(节点值不重复,从 1 到 n)的树及一条附加的有向边构成。附加的边包含在 1 到 n 中的两个不同顶点间,这条附加的边不属于树中已存在的边。
    结果图是一个以边组成的二维数组 edges 。 每个元素是一对 [ui, vi],用以表示 有向 图中连接顶点 ui 和顶点 vi 的边,其中 ui 是 vi 的一个父节点。
    返回一条能删除的边,使得剩下的图是有 n 个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。

一样使用并查集


class Solution {
    
    
private:
    static const int N = 1010; // 如题:二维数组大小的在3到1000范围内
    int father[N];
    int n; // 边的数量
    // 并查集初始化
    void init() {
    
    
        for (int i = 1; i <= n; ++i) {
    
    
            father[i] = i;
        }
    }
    // 并查集里寻根的过程
    int find(int u) {
    
    
        return u == father[u] ? u : father[u] = find(father[u]);
    }
    // 将v->u 这条边加入并查集
    void join(int u, int v) {
    
    
        u = find(u);
        v = find(v);
        if (u == v) return ;
        father[v] = u;
    }
    // 判断 u 和 v是否找到同一个根
    bool same(int u, int v) {
    
    
        u = find(u);
        v = find(v);
        return u == v;
    }
    // 在有向图里找到删除的那条边,使其变成树
    vector<int> getRemoveEdge(const vector<vector<int>>& edges) {
    
    
        init(); // 初始化并查集
        for (int i = 0; i < n; i++) {
    
     // 遍历所有的边
            if (same(edges[i][0], edges[i][1])) {
    
     // 构成有向环了,就是要删除的边
                return edges[i];
            }
            join(edges[i][0], edges[i][1]);
        }
        return {
    
    };
    }

    // 删一条边之后判断是不是树
    bool isTreeAfterRemoveEdge(const vector<vector<int>>& edges, int deleteEdge) {
    
    
        init(); // 初始化并查集
        for (int i = 0; i < n; i++) {
    
    
            if (i == deleteEdge) continue;
            if (same(edges[i][0], edges[i][1])) {
    
     // 构成有向环了,一定不是树
                return false;
            }
            join(edges[i][0], edges[i][1]);
        }
        return true;
    }
public:

    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
    
    
        int inDegree[N] = {
    
    0}; // 记录节点入度
        n = edges.size(); // 边的数量
        for (int i = 0; i < n; i++) {
    
    
            inDegree[edges[i][1]]++; // 统计入度
        }
        vector<int> vec; // 记录入度为2的边(如果有的话就两条边)
        // 找入度为2的节点所对应的边,注意要倒叙,因为优先返回最后出现在二维数组中的答案
        for (int i = n - 1; i >= 0; i--) {
    
    
            if (inDegree[edges[i][1]] == 2) {
    
    
                vec.push_back(i);
            }
        }
        // 处理图中情况1 和 情况2
        // 如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
        if (vec.size() > 0) {
    
    
            if (isTreeAfterRemoveEdge(edges, vec[0])) {
    
    
                return edges[vec[0]];
            } else {
    
    
                return edges[vec[1]];
            }
        }
        // 处理图中情况3
        // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
        return getRemoveEdge(edges);

    }
};


猜你喜欢

转载自blog.csdn.net/u013354486/article/details/117381420
今日推荐