领扣第4场双周赛题解+为第4道题目贡献的测试样例

算法小白malloc利于低端基础理论解决领扣周赛问题的过程,并在最后附上第4道题目Divide Array Into Increasing Sequences(将数组分成几个递增序列)找到的hack数据,不喜勿喷。。。。。。


1118. 一个月有多少天(Easy)

          指定年份 Y 和月份 M,请你帮忙计算出该月一共有多少天。

示例:

          输入:Y = 1992, M = 7

          输出:31

解析:基础题目,使用一个switch语句即可实现。依据为:一三五七八十腊,三十一天永不差。闰年二月为28天,平年二月为29天。

int numberOfDays(int Y, int M) 
{
    switch (M)
    {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:return 31;
        case 2:{if(Y % 400 == 0 || (Y % 4 == 0 && Y % 100 != 0))
                return 29;
               else return 28;}
        default:return 30;
    }
}

 


1119. 给你一个字符串 S,请你删去其中的所有元音字母( 'a''e''i''o''u'),并返回这个新字符串(Easy)

示例:

         输入:"leetcodeisacommunityforcoders"

         输出:"ltcdscmmntyfrcdrs"

解析:基础题目,可以通过遍历字符串依次判断每个字母是否为元音实现。删除元音时有两种方式,一种为在原字符串上通过STL的erase函数进行删除,但该方法的时间复杂度较高,亲测运行超时;另一种为定义一个新的字符串,若遍历时的当前字符串为非元音,则将其追加到新的字符串上。

//AC代码
string removeVowels(string S) 
{
    for (int i = 0; i < S.size(); ++i)
    {
        if (S[i] == 'a' || S[i] == 'e' || S[i] == 'i' || S[i] == 'o' || S[i] == 'u')
            S.erase(S.begin() + i);
            --i;
    }
    return S;
}
//运行超时的代码
string removeVowels(string S) 
{
    string temp;
    for (int i = 0;i < S.size();++i)
    {
        if(!(S[i] == 'a' || S[i] == 'e' || S[i] == 'i' || S[i] == 'o' || S[i] == 'u'))
           temp += S[i];
    }
    return temp;
}

1120. 子树的最大平均值(Medium)

给你一棵二叉树的根节点 root,找出这棵树的 每一棵 子树的 平均值 中的 最大 值。

子树是树中的任意节点和它的所有后代构成的集合。

树的平均值是树中节点值的总和除以节点数。

示例:

          

输入:[5,6,1]
输出:6.00000
解释: 
以 value = 5 的节点作为子树的根节点,得到的平均值为 (5 + 6 + 1) / 3 = 4。
以 value = 6 的节点作为子树的根节点,得到的平均值为 6 / 1 = 6。
以 value = 1 的节点作为子树的根节点,得到的平均值为 1 / 1 = 1。
所以答案取最大值 6。

解析:如果采用某种遍历顺序遍历整个二叉树,并计算以当前遍历节点为根的子树的平均值,那么运行时间对于节点数目将是指数型的增长,毫无疑问会运行超时。本文采用的方法通过两次后续遍历即可解决该问题。

第一次后续遍历(dfs_value)用来将节点值val替换成以该节点为根的子树的总值,实现方式为:后续遍历,若当前节点的左子树不为空则将其值加到当前节点上,同样若当前节点的右子树不为空则将其值加到当前节点上;由于后续遍历左->右->中的遍历顺序,所以对当前节点值进行处理之前,其左右节点的值已经被处理过(即存储的是以该节点为根的子树的总值)。

第二次后续遍历(dfs_cnt)用来将节点值val替换成以该节点为根的子树的节点总数,同时计算出其以该节点为根的子树的均值用来与需要返回的值result进行比较,若其大于result,则用其替代result。具体实现为:后续遍历,遍历到某一个节点时(可将其称之为当前节点),可定义整型变量cnt用来暂时存储以该节点为根的子树的节点总数(即1 + 左节点val + 右节点val),用当前节点的val值(这时未被替换,仍为以该节点为根的子树的总值)除以cnt和result进行比较进行相应的处理,比较完成后将当前节点的val值替换成cnt以供其父节点使用。

最后的result值即为需要返回的结果。

static void dfs_value(TreeNode* root)
{
    if (root->left)
        dfs_value(root->left);
    if (root->right)
        dfs_value(root->right);

    if(root->left != NULL)
        root->val += root->left->val;
    if(root->right != NULL)
        root->val += root->right->val;
}

static void dfs_cnt(TreeNode* root, double &result)
{
    if (root->left)
        dfs_cnt(root->left, result);
    if (root->right)
        dfs_cnt(root->right, result);

    int cnt = 1;
    if(root->left != NULL)
        cnt += root->left->val;   
    if(root->right != NULL)
        cnt += root->right->val;

    if (root->val * 1.0 / cnt > result)
        result = root->val * 1.0 / cnt;
    root->val = cnt;
}

double maximumAverageSubtree(TreeNode* root) 
{
    double result = 0;
    dfs_value(root);
    dfs_cnt(root, result);
    return result;
}

1121. 将数组分成几个递增序列 

给你一个 非递减 的正整数数组 nums 和整数 K,判断该数组是否可以被分成一个或几个 长度至少 为 K 的 不相交的递增子序列

示例:

输入:nums = [1,2,2,3,3,4,4], K = 3
输出:true
解释:
该数组可以分成两个子序列 [1,2,3,4] 和 [2,3,4],每个子序列的长度都至少是 3。

解析:给定的数组为非递减数组,即数组中可能会有相同的元素值;而要求将其分为一个或几个长度至少为K的不相交的递增子序列,即该子序列中不能包含重复的元素值。现将重复个数最多的元素的个数称为minDigitNum,则由以上分析可知至少应将其分为minDigitNum组,每组至少K个;如果没有重复的元素,则将其分为1组即可。

现在就需要根据给定元素的重复情况计算出要组成minDigitNum组字符列至少需要多少个元素,并将其与给定的数量进行比较,若小于等于给定的数量则可以满足要求,否则不能。

现将给定元素重复组数定义为num,则计算最少需要元素数量的方法为:

若K大于等于num则至少需要K*minDigitNum个元素,可简单理解为下图:

若K小于num,则对重复的元素按重复的个数从少到多进行排序,至少需要K*minDigitNum加上前num - K 种重复元素的总数,可理解为下图:

bool canDivideIntoSubsequences(vector<int>& nums, int K) {

    vector<int>repEleNum;
    int start = -1;

    for (int i = 0;i < nums.size() - 1;++i)
    {
        if (nums[i] == nums[i + 1] && start == -1)
            start = i;

        if (nums[i] != nums[i + 1] && start != -1)
        {
            repEleNum.push_back(i + 1 - start);
            start = -1;
        }
    }

    if (start != -1)
        repEleNum.push_back(nums.size() - start);

    sort(repEleNum.begin(), repEleNum.end());

    int minDigitNum = repEleNum.size() > 0 ? K * *(repEleNum.rbegin()) : 1;

    if(K < repEleNum.size())
    {
        for (int i = 0;i < repEleNum.size() - K;++i)
            minDigitNum += repEleNum[i];
    }

    if(minDigitNum <= nums.size())
        return true;
    else
        return false;
}

关于hack数据

起初将需要的最少元素数目简单的理解为了上面的minDigit(下面的代码中为maxReNumLen)乘 max{K, num}(num在下面的代码中为repeatNum),同样通过了测试。但提交的代码并不是完全正确的,例如

Input:[1,2,2,2,3,3,4,4] 2

Output应该为true,但是提交的AC代码返回了false。

代码实现如下:

class Solution {
public:
    bool canDivideIntoSubsequences(vector<int>& nums, int K) {

        int repeatNum = 0, start = -1, maxReNumLen = 0;

        for (int i = 0;i < nums.size() - 1;++i)
        {
            if (nums[i] == nums[i + 1] && start == -1)
            {
                start = i;
                ++repeatNum;
            }

            if (nums[i] != nums[i + 1] && start != -1)
            {
                if (i + 1 - start > maxReNumLen)
                    maxReNumLen = i + 1 - start;
                start = -1;
            }
        }
        if (start != -1)
        {
            if (nums.size() - start > maxReNumLen)
                maxReNumLen = nums.size() - start;
        }

        if(K > repeatNum)
            repeatNum = K;

        if((long)repeatNum * maxReNumLen <= nums.size())
            return true;
        else
            return false;
    }
};

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MallocLu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值