leetcode解题思路分析(一百零四)888 - 894 题

本文提供了五个经典算法问题的解决方案,包括糖果交换、二叉树构造、模式匹配、子序列宽度之和及表面积计算等问题,通过巧妙的方法提高了解题效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 公平的糖果交换
    爱丽丝和鲍勃拥有不同总数量的糖果。给你两个数组 aliceSizes 和 bobSizes ,aliceSizes[i] 是爱丽丝拥有的第 i 盒糖果中的糖果数量,bobSizes[j] 是鲍勃拥有的第 j 盒糖果中的糖果数量。两人想要互相交换一盒糖果,这样在交换之后,他们就可以拥有相同总数量的糖果。一个人拥有的糖果总数量是他们每盒糖果数量的总和。返回一个整数数组 answer,其中 answer[0] 是爱丽丝必须交换的糖果盒中的糖果的数目,answer[1] 是鲍勃必须交换的糖果盒中的糖果的数目。如果存在多个答案,你可以返回其中 任何一个 。题目测试用例保证存在与输入对应的答案。

用哈希表存储所有的数然后查找即可

class Solution {
public:
    vector<int> fairCandySwap(vector<int>& aliceSizes, vector<int>& bobSizes) {
        int sumA = accumulate(aliceSizes.begin(), aliceSizes.end(), 0);
        int sumB = accumulate(bobSizes.begin(), bobSizes.end(), 0);
        int delta = (sumA - sumB) / 2;
        unordered_set<int> rec(aliceSizes.begin(), aliceSizes.end());
        vector<int> ans;
        for (auto& y : bobSizes) {
            int x = y + delta;
            if (rec.count(x)) {
                ans = vector<int>{x, y};
                break;
            }
        }
        return ans;
    }
};


  1. 根据前序和后序遍历构造二叉树
    返回与给定的前序和后序遍历匹配的任何二叉树。

递归查找,首先根据前序确定左子树的根,然后根据后续确定左子树的长度。由此可以递归左子树,直至只有一个节点。右子树同理。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* constructFromPrePost(vector<int>& pre, vector<int>& post) {
        return helper(pre, post, 0, pre.size() - 1, 0, post.size() - 1);
    }

    TreeNode* helper(vector<int>& pre, vector<int>& post, int prestart, int preend, int poststart, int postend) {
        if (prestart > preend) {
            return NULL;
        }
        TreeNode* root = new TreeNode(pre[prestart]);
        if (prestart == preend) { //只有一个,直接返回
            return root;
        }
        int i = poststart; 
        while (i < postend && post[i] != pre[prestart + 1]) { //确定分界点,左子树的根节点
            i++; 
        }
        int len = i - poststart + 1; //左子树长度
        root->left = helper(pre, post, prestart + 1, prestart + len, poststart, i);
        root->right = helper(pre, post, prestart + 1 + len, preend, i + 1, postend - 1);
        return root;
    }
};


  1. 查找和替换模式
    你有一个单词列表 words 和一个模式 pattern,你想知道 words 中的哪些单词与模式匹配。如果存在字母的排列 p ,使得将模式中的每个字母 x 替换为 p(x) 之后,我们就得到了所需的单词,那么单词与模式是匹配的。(回想一下,字母的排列是从字母到字母的双射:每个字母映射到另一个字母,没有两个字母映射到同一个字母。)返回 words 中与给定模式匹配的单词列表。你可以按任何顺序返回答案。

轮询words,每个域pattern单独比较字符规则,如果相同则说明是一个解。

class Solution {
public:
    vector<string> findAndReplacePattern(vector<string>& words, string pattern) {
        vector<string>ans;
        int n=pattern.size();
        for(auto&s:words){
            if(s.size()!=n)continue;
            bool flag=false;
            unordered_map<char,char>mp1,mp2;
            for(int i=0;i<n;i++){
                char c1=s[i],c2=pattern[i];
                if(mp1.count(c1)&&mp1[c1]!=c2){
                    flag=true;
                    break;
                }
                mp1[c1]=c2;
                if(mp2.count(c2)&&mp2[c2]!=c1){
                    flag=true;
                    break;
                }
                mp2[c2]=c1;
            }
            if(flag==false)ans.push_back(s);
        }
        return ans;
    }
};


  1. 子序列宽度之和
    一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和 。由于答案可能非常大,请返回对 109 + 7 取余 后的结果。子序列 定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数组。例如,[3,6,2,7] 就是数组 [0,3,1,6,2,2,7] 的一个子序列。

推导数学公式并求解

typedef long long ll;
const ll MOD = 1e9 + 7;

class Solution {
public:
    int sumSubseqWidths(vector<int>& A) {
        int n = A.size();
        sort(A.begin(), A.end());
        vector<ll> two(n + 1);
        two[0] = 1;
        for (int i = 1; i <= n; ++i)
            two[i] = (two[i - 1] << 1) % MOD;
        ll ans = 0;
        for (int i = 0; i < n; ++i) {
            int left = i;
            int right = n - i - 1;
            ans = (ans + (two[left] - two[right]) * A[i]) % MOD;
        }
        return (ans + MOD) % MOD;
    }
};


  1. 三维形体的表面积
    给你一个 n * n 的网格 grid ,上面放置着一些 1 x 1 x 1 的正方体。每个值 v = grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。放置好正方体后,任何直接相邻的正方体都会互相粘在一起,形成一些不规则的三维形体。请你返回最终这些形体的总表面积。

挨个统计即可

class Solution {
public:
    int surfaceArea(vector<vector<int>>& grid) {
        int dr[]{0, 1, 0, -1};
        int dc[]{1, 0, -1, 0};

        int N = grid.size();
        int ans = 0;

        for (int r = 0; r < N; ++r) {
            for (int c = 0; c < N; ++c) {
                if (grid[r][c] > 0) {
                    ans += 2;
                    for (int k = 0; k < 4; ++k) {
                        int nr = r + dr[k];
                        int nc = c + dc[k];
                        int nv = 0;
                        if (0 <= nr && nr < N && 0 <= nc && nc < N) {
                            nv = grid[nr][nc];
                        }

                        ans += max(grid[r][c] - nv, 0);
                    }
                }
            }
        }

        return ans;
    }
};


  1. 特殊等价字符串组
    给你一个字符串数组 words。现在规定,words 的 一组特殊等价字符串 就是 words 的一个同时满足下述条件的非空子集:该组中的每一对字符串都是 特殊等价 的
    该组字符串已经涵盖了该类别中的所有特殊等价字符串,容量达到理论上的最大值(也就是说,如果一个字符串不在该组中,那么这个字符串就 不会 与该组内任何字符串特殊等价)
    返回 words 中 特殊等价字符串组 的数量。

按奇偶分类存俩string,然后排序后拼接,放入set中,计数即可

class Solution {
public:
    int numSpecialEquivGroups(vector<string>& words) {
        unordered_set<string>mp;
        for(auto&s:words){
            string odd,even;
            for(int i=0;i<s.size();i++){
                if(i&1)odd.push_back(s[i]);
                else even.push_back(s[i]);
            }
            sort(odd.begin(),odd.end());
            sort(even.begin(),even.end());
            string connect=odd+"#"+even;
            mp.insert(connect);
        }
        return mp.size();
    }
};


  1. 所有可能的满二叉树
    满二叉树是一类二叉树,其中每个结点恰好有 0 或 2 个子结点。返回包含 N 个结点的所有可能满二叉树的列表。 答案的每个元素都是一个可能树的根结点。答案中每个树的每个结点都必须有 node.val=0。你可以按任何顺序返回树的最终列表。

递归迭代

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
 
class Solution {
private:
    unordered_map<int, vector<TreeNode*>> num2nodes;
public:
    vector<TreeNode*> allPossibleFBT(int n) {
        if (num2nodes.find(n) == num2nodes.end())
        {
            vector<TreeNode*> res;
            if (n == 1)
            {
                res.push_back(new TreeNode(0));
            }
            else if (n % 2 == 1)
            {
                // 大于等于3的奇数
                // 去按照不同取值构建关系
                for (int l = 0; l < n; ++l)
                {
                    int r = n - 1 - l;
                    for (TreeNode* left : allPossibleFBT(l))
                    {
                        for (TreeNode* right : allPossibleFBT(r))
                        {
                            // 构建一个新的结点,并且设置left和right
                            res.push_back(new TreeNode(0, left, right));
                        }
                    }
                }
            }
            num2nodes[n] = res;
        }

        return num2nodes[n];
    }
};


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ch_ty

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值