[C++]力扣周赛第283场题解

Excel 表中某个范围内的单元格

题目描述

Excel 表中的一个单元格 (r, c) 会以字符串 “” 的形式进行表示,其中:
<col> 即单元格的列号 c 。用英文字母表中的 字母 标识。 例如,第 1 列用 ‘A’ 表示,第 2 列用 ‘B’ 表示,第 3 列用 ‘C’ 表示,以此类推。 <row> 即单元格的行号 r 。第 r 行就用 整数 r 标识。 给你一个格式为 “<col1><row1>:<col2><row2>” 的字符串 s ,其中 <col1> 表示 c1 列,<row1> 表示 r1 行,<col2> 表示 c2 列,<row2> 表示 r2 行,并满足 r1 <= r2 且 c1 <= c2 。找出所有满足 r1 <= x <= r2 且 c1 <= y <= c2 的单元格,并以列表形式返回。单元格应该按前面描述的格式用 字符串表示,并以 非递减 顺序排列(先按列排,再按行排)。

示例 1:
在这里插入图片描述
输入:s = “K1:L2”
输出:[“K1”,“K2”,“L1”,“L2”]
解释: 上图显示了列表中应该出现的单元格。红色箭头指示单元格的出现顺序。

示例2:
在这里插入图片描述
输入:s = “A1:F1”
输出:[“A1”,“B1”,“C1”,“D1”,“E1”,“F1”]
解释:上图显示了列表中应该出现的单元格。 红色箭头指示单元格的出现顺序。

提示:
s.length == 5
‘A’ <= s[0] <= s[3] <= ‘Z’
‘1’ <= s[1] <= s[4] <= ‘9’
s由大写英文字母、数字、和 ‘:’ 组成

思路

看到这道简单题,没什么好说的,上来直接搜就行了,保存起始坐标R1,C1,终点坐标R2,C2遍历每一个用ans保存下每一个单元格的坐标即可。

代码

class Solution {
public:
    vector<string> cellsInRange(string s) {
        vector<string> ans;
        for (char i=s[0];i<=s[3];i++) {
            for(char j=s[1];j<=s[4];j++) {
                string tmp="";
                tmp+=i;
                tmp+=j;
                ans.push_back(tmp);
            }
        }
        return ans;
    }
};

复杂度分析:
时间复杂度:O(mn),其中m为行差,n为列差
空间复杂度:O(mn),其中m为行差,n为列差,我们需要保存共mn个单元格

向数组中追加 K 个整数

题目描述

给你一个整数数组 nums 和一个整数 k 。请你向 nums 中追加 k 个未出现在 nums 中的、互不相同的 正整数,并使结果数组的元素和最小 。返回追加到 nums 中的 k 个整数之和。

示例 1:
输入:nums = [1,4,25,10,25], k = 2
输出:5
解释:在该解法中,向数组中追加的两个互不相同且未出现的正整数是 2
和 3 。 nums 最终元素和为 1 + 4 + 25 + 10 + 25 + 2 + 3 = 70 ,这是所有情况中的最小值。
所以追加到数组中的两个整数之和是 2 + 3 = 5 ,所以返回 5 。

示例2:
输入:nums = [5,6], k = 6
输出:25
解释:在该解法中,向数组中追加的两个互不相同且未出现的正整数是 1 、2 、3 、4 、7 和 8 。
nums 最终元素和为 5 + 6 + 1 + 2 + 3 + 4 + 7 + 8 = 36 ,这是所有情况中的最小值。
所以追加到数组中的两个整数之和是 1 + 2 + 3 + 4 + 7 + 8 = 25 ,所以返回 25 。

提示:
1 <= nums.length <= 105
1 <= nums[i], k <= 109

思路

我们对题目进行一个小解读,就是要在数组nums里插入k个数,这k个数没在nums里出现过,而且这k个数尽可能要小。
既然要使得k个数尽可能小,首先我们要对nums数组进行排序,题目没有说nums是一个严格的只出现一次的数组,所以我们要完成去重工作,既要去重,又要排序,我们不难想到用C++内置的STL——Set进行预处理。

	set<int> tmp;
    tmp.insert(nums.begin(),nums.end());
    nums.assign(tmp.begin(),tmp.end());

对nums进行填补的时候,可以预想到3种不同的情况:

  1. 填补间隔小于等于k
  2. 填补间隔大于k
  3. 对nums数组遍历完后k还有剩余

我们声明一个变量key,用来保存当前可填补的最小边界。
nums[i]-1即是当前可填补的最大边界。
在未进行遍历时,题目要求填补的数字必须为正整数所以key的初值为1。

对于第1种情况,我们从key填补到nums[i]-1,k还有剩余,全部填充。
答案增加key~nums[i]-1的和。
对于数组[1,4],k=5,间隔为3,填完k还有剩余。

对于第2种情况,我们需要从key填补k个数。
答案增加key~key+k-1的和。
对于数组[1,4],k=1,间隔为3,填完k没有剩余,填充工作结束。

对于第3种情况,我们需要从nums[n-1]+1填补最后剩余的k个数。
答案增加nums[n-1]+1~nums[n-1]+k的和。
对于数组[1,2],k=3,从3开始填充3个数 ,填充工作结束。

不难发现,每次我们对ans求和的时候都是一段连续的等差数列公差为1的数列求和,我们可以写一个函数用来对等差数列求和,只要保存数列的起始位置和结束位置就可以用O(1)的算法完成求和统计。

公差为1的数列求和公式为:(首项+末项)项数/2

    long long sum(int l,int r)
    {
        return (long long)(l+r)*(long long )(r-l+1)/2;
    }

到这里,这道题目已经分析完了,剩下的就是手速了

代码

class Solution {
public:
    long long sum(int l,int r)
    {
        return (long long)(l+r)*(long long )(r-l+1)/2;
    }
    long long minimalKSum(vector<int>& nums, int k) {
        long long ans=0;
        set<int> tmp;
        tmp.insert(nums.begin(),nums.end());
        nums.assign(tmp.begin(),tmp.end());//去重
        int n=nums.size(),key=1;
        for(int i=0;i<n;i++)//遍历数组已有的值
        {
            if (key==nums[i])//维护填补边界,防止填补边界出现在数组中
            {
                key=nums[i]+1;
                continue;
            }
            if (nums[i]-key+1<=k)//第1种情况,间隔<=k
            {
                ans+=sum(key,nums[i]-1);
                k-=nums[i]-key;//k减少已填充的数据
                key=nums[i]+1;//维护下一次填充边界
            }
            else//第2种情况,间隔>k
            {
                ans+=sum(key,key+k-1);
                k=0;
                break;//k已取尽,跳出循环
            }
        }
        if (k>0) ans+=sum(nums[n-1]+1,nums[n-1]+k);//第3种情况,填充完整个数组后还有剩余
        return ans;
    }
};

复杂度分析:
时间复杂度:O(n),其中n为nums的长度
空间复杂度:O(n),我们需要利用大小为n的集合set对nums排序和去重

根据描述创建二叉树

题目描述

给你一个二维整数数组 descriptions ,其中 descriptions[i] = [parenti, childi, isLefti] 表示 parenti是 childi i在 二叉树 中的 父节点,二叉树中各节点的值 互不相同 。此外:

如果 isLefti == 1 ,那么 childi 就是 parenti 的左子节点。
如果 isLefti == 0 ,那么 childi 就是 parenti 的右子节点。
请你根据 descriptions 的描述来构造二叉树并返回其 根节点 。
测试用例会保证可以构造出 有效 的二叉树。

示例1:
在这里插入图片描述
输入:descriptions = [[20,15,1],[20,17,0],[50,20,1],[50,80,0],[80,19,1]]
输出:[50,20,80,15,17,19]
解释:根节点是值为 50 的节点,因为它没有父节点。
结果二叉树如上图所示。

示例2:
在这里插入图片描述
输入:descriptions = [[1,2,1],[2,3,0],[3,4,1]]
输出:[1,2,null,null,3,4]
解释:根节点是值为 1 的节点,因为它没有父节点。
结果二叉树如上图所示。

提示:
1 <= descriptions.length <= 104
descriptions[i].length == 3
1 <= parenti, childi <= 105
0 <= isLefti <= 1
descriptions 所描述的二叉树是一棵有效二叉树

思路

对于这道题目,我们可以拆分成几个模块去思考:
哈希表+建树+确定根节点。
哈希表保存二叉树网,确定每一个结点映射的左右孩子,以此模拟建树。
对于根节点,不妨从根节点的特性去考虑,题目规定二叉树是有效的,那么根节点是不会被指示为孩子的,我们可以记录每一个结点是否被标记为孩子,唯一没被标记为孩子的结点就是根节点,我们可以利用另一个哈希表去记录每一个结点被标记为孩子的次数。

代码

class Solution {
public:
    TreeNode* createBinaryTree(vector<vector<int>>& descriptions) {    
        unordered_map<int,TreeNode*> um1;
        unordered_map<int,int> um2;//保存每个结点作为孩子的次数,为0次的为最终根节点
        for (vector<int>& i:descriptions)
        {//i[0]为parent[i] i[1]为child[i] i[2]为left[i]
            if (!um1.count(i[0])) um1[i[0]] = new TreeNode(i[0]);
            if (!um1.count(i[1])) um1[i[1]] = new TreeNode(i[1]);//产生新的结点,建立映射
            if (i[2]) um1[i[0]]->left=um1[i[1]];//为左孩子的时候,建立左孩子的映射
            else um1[i[0]]->right=um1[i[1]];//建立右孩子的映射
            um2[i[1]]++;
        }
        for (auto [i,node]:um1)
            if (um2[i]==0) return node;
        return NULL;
    }
};

复杂度分析:
时间复杂度:O(n),其中n为descriptions的长度。
空间复杂度:O(n),其中n为descriptions的长度,我们需要用大小为n的哈希表记录结点和其映射。

替换数组中的非互质数

题目描述

给你一个整数数组 nums 。请你对数组执行下述操作:
从 nums 中找出任意两个相邻非互质 数。
如果不存在这样的数,终止这一过程。
否则,删除这两个数,并替换为它们的最小公倍数(Least Common Multiple,LCM)。
只要还能找出两个相邻的非互质数就继续重复这一过程。
返回修改后得到的 最终 数组。可以证明的是,以任意顺序替换相邻的非互质数都可以得到相同的结果。
生成的测试用例可以保证最终数组中的值小于或者等于108
两个数字 x 和 y 满足非互质数的条件是:GCD(x, y)>1 ,其中 GCD(x, y) 是 x 和 y 的 最大公约数 。

示例1:
输入:nums = [6,4,3,2,7,6,2]
输出:[12,7,6]
解释:
(6, 4) 是一组非互质数,且 LCM(6, 4) = 12 。得到 nums = [12,3,2,7,6,2] 。
(12, 3) 是一组非互质数,且 LCM(12, 3) = 12 。得到 nums = [12,2,7,6,2] 。
(12, 2) 是一组非互质数,且 LCM(12, 2) = 12 。得到 nums = [12,7,6,2] 。
(6, 2) 是一组非互质数,且 LCM(6, 2) = 6 。得到 nums = [12,7,6] 。 现在,nums 中不存在相邻的非互质数。 因此,修改后得到的最终数组是 [12,7,6] 。 注意,存在其他方法可以获得相同的最终数组。

示例2:
输入:nums = [2,2,1,1,3,3,3]
输出:[2,1,1,3]
解释:
(3, 3) 是一组非互质数,且 LCM(3, 3) = 3 。得到 nums = [2,2,1,1,3,3] 。
(3, 3) 是一组非互质数,且 LCM(3, 3) = 3 。得到 nums = [2,2,1,1,3] 。
(2, 2) 是一组非互质数,且 LCM(2, 2) = 2 。得到 nums = [2,1,1,3] 。
现在,nums 中不存在相邻的非互质数。
因此,修改后得到的最终数组是 [2,1,1,3] 。
注意,存在其他方法可以获得相同的最终数组。

提示:
1 <= nums.length <= 105
1 <= nums[i] <= 105
生成的测试用例可以保证最终数组中的值小于或者等于 108

思路

看到这道题目,我的第一反应是消消乐,如果满足条件则合并,然后会继续掉落别的颜色的方块,最终结果为不能消除堆积方块堆,在这道题,我们可以把数组旋转90°看,每一次往数组里掉落数字,如果掉落的数字是与顶部非互质的,合并消除,是互质的,就保留,所以想到应用栈的方法去处理,但是在这道题中,因为要同时考虑到nums[n-1]和nums[n-2]的数的GCD,我们可以使用一个数组来替代一个栈(Stack)

代码

class Solution {
public:
    vector<int> replaceNonCoprimes(vector<int>& nums) {
        int j=0;
        vector<int> ans;
        for(int i:nums)
        {
            ans.push_back(i);//当前元素入栈
            int n=ans.size();//由于每一次落入数字后,栈的size都会发生变化,需要动态更新
            while (n>1&&__gcd(ans[n-1],ans[n-2])>1)//栈顶元素和栈顶下一个元素非互质则合并
            {
                int front=ans.back();//保存栈顶
                ans.pop_back();//弹出栈顶,当前栈顶为ans[n-2]
                ans.back()*=front/__gcd(ans.back(),front);//nums[n-2]保存为lcm(nums[n-1],nums[n-2])
                n--;//合并后减少栈的元素个数
            }
        }
        return ans;
    }
};

复杂度分析:
时间复杂度:O(n),其中n为nums数组的长度
空间复杂度:O(n),其中n为nums数组的长度

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Capzera

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

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

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

打赏作者

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

抵扣说明:

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

余额充值