LeetCode 刷题小结杂谈(一)

这节我们来汇总一些LeetCode题,我也没有对它们分类,索性称为杂谈吧。



这个系列介绍的题我没有赋予标签,虽然它们都是有具体的方法,但是之所以单独拿出来,是因为它们带有一些独特的内涵在其中,还是值得细细“品味”的。

插张图,很多题倘若没有思路,不妨暴力求解出发,不断优化,往往会得到不错的效果。

就像这图所示,我们先介绍一道回溯加剪枝的题。

可能语焉不详,还请多多海涵。

组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

来源。

某种程度上说,个人觉得这个题其实算穷举,数字可以无限制使用。但是真的去穷举不仅仅不好写,而且时空代价高。
这个题可以算是树的搜索,满足条件的是一条路径,但是列举所有的情况不现实,我们需要剪枝。

在解题区,这位大佬已经说得很清楚了,传送门。

我个人的理解,也写在了我个人的我的解题中。
若有不对还请多多指出。

我们先给数组排序,从candidates某一个元素candidates[i]开始,如果它比target小,candidates[i]可以暂存到path中,从candidates[i]处继续开始dfs,因为candidates 中的数字可以无限制重复被选取。如果target==0,这是一个满足条件的组合。如果target比candidates[i]还小了,说明这次暂存的元素不合理,把它弹出(pop_back()),那它就会从candidates的下一个元素出发,继续dfs,直到结束整个过程。

程序如下:

class Solution {
public:
    vector path;
    vector> res;
    vector> combinationSum(vector& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        dfs(candidates,target,0);
        return res;
    }
    void dfs(vector& candidates, int target,int start){
        if(target == 0){
            res.push_back(path);
            return ;
        }
        for(int i=start;i= candidates[i]){
                path.push_back(candidates[i]);
                dfs(candidates,target-candidates[i],i);
                path.pop_back();
            }
        }
    }
};

接下来的这个题就很有意思了。

石子游戏

亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。

游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。

亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。

假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

示例:

输入:[5,3,4,5]
输出:true
解释:
亚历克斯先开始,只能拿前 5 颗或后 5 颗石子 。
假设他取了前 5 颗,这一行就变成了 [3,4,5] 。
如果李拿走前 3 颗,那么剩下的是 [4,5],亚历克斯拿走后 5 颗赢得 10 分。
如果李拿走后 5 颗,那么剩下的是 [3,4],亚历克斯拿走后 4 颗赢得 9 分。
这表明,取前 5 颗石子对亚历克斯来说是一个胜利的举动,所以我们返回 true 。

提示:

2 <= piles.length <= 500
piles.length 是偶数。
1 <= piles[i] <= 500
sum(piles) 是奇数。

来源。

这个题要是用贪心算法做,那就不行了。
以我一开始的想法为例:

class Solution {
public:
    bool stoneGame(vector& piles) {
        if(piles.size()<2){
            return true;
        }
        int sum_ya=0;
        int sum_li=0;
        int p1=0;
        int p2=piles.size()-1;
        for(int i=1;i<=piles.size();i++){
            if(i%2==1){
                if(piles[p1]sum_li;
    }
};

能通过很多样例,但是,局部最优有时候却不是全局最优。
看一个样例:

[2,1,10,3]

亚历克斯要是拿局部最优3,那10就被李拿走了,他就输了。
但是啊,亚历克斯先手,他可以拿2,3被李拿走了,他再拿10,赢了。

那问题来了,我们该怎么做呢,动态规划是一种方法,这个题的dp解法将在以后的博客中给出,这里我们不用这种方法。

倘若你要给出一些样例,会发现,好像先手的亚历克斯总能赢,细化一点问题。
两个石子,先手肯定赢,那四个石子呢,细心推理发现,先手还能赢,再往后呢...

我们发现似乎先手总能赢,那事实确如此,有兴趣的可以去证明或者查看解题评论区。

那结果:

class Solution {
public:
    bool stoneGame(vector& piles) {
        return true;
    }
};

动态规划解法会在之后博客给出。

可怜的小猪

有 1000 只水桶,其中有且只有一桶装的含有毒药,其余装的都是水。它们从外观看起来都一样。如果小猪喝了毒药,它会在 15 分钟内死去。

问题来了,如果需要你在一小时内,弄清楚哪只水桶含有毒药,你最少需要多少只猪?

回答这个问题,并为下列的进阶问题编写一个通用算法。

进阶:

假设有 n 只水桶,猪饮水中毒后会在 m 分钟内死亡,你需要多少猪(x)就能在 p 分钟内找出 “有毒” 水桶?这 n 只水桶里有且仅有一只有毒的桶。

提示:

可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。
小猪喝完水后,必须有 m 分钟的冷却时间。在这段时间里,只允许观察,而不允许继续饮水。
任何给定的桶都可以无限次采样(无限数量的猪)。

来源。

这个题是一道难题,难点不是程序,而是思路,思路对了,很简单。

量子力学很火热,那这个题中的猪也可以算是量子状态下的猪,这个题涉及到信息论方面的知识。

想明白一个问题,一只猪在规定时间内做多能帮你测试几桶水?
清楚了这个,猪的数目就简单了
先给出结果,仔细想一想就会恍然大悟了。

详细过程可以见官方解答,传送门。

class Solution {
public:
    int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
        return ceil(log(buckets)/(log(int(minutesToTest/minutesToDie)+1)));
    }
};

求众数 II

给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。

示例 1:

输入: [3,2,3]
输出: [3]
示例 2:

输入: [1,1,1,3,3,2,2,2]
输出: [1,2]

来源。

没有这个要求,其实不难,用map很容易出结果,奇怪的是这种方法居然AC了。。。

class Solution {
public:
    vector majorityElement(vector& nums) {
        map m;
        int len = nums.size();
        for(int i=0;i res;
        for(auto _m : m){
            if(_m.second > len/3){
                res.push_back(_m.first);
            }
        }
        return res;
    }
};

满足这个要求,这个题还是不好做,这其中蕴含一种经典的算法,摩尔投票。
详细过程可见评论区,传送门。

归纳一下便是:
如果至多选一个代表,那他的票数至少要超过一半(⌊ 1/2 ⌋)的票数;
如果至多选两个代表,那他们的票数至少要超过⌊ 1/3 ⌋的票数;
如果至多选m个代表,那他们的票数至少要超过⌊ 1/(m+1) ⌋的票数。
之后碰到这样的问题,而且要求达到线性的时间复杂度以及常量级的空间复杂度,直接套上摩尔投票法。



本节内容暂告一段落,之后讲继续更新。

你可能感兴趣的:(LeetCode 刷题小结杂谈(一))