leetCode进阶算法题+解析(五十九)

数组嵌套

题目:索引从0开始长度为N的数组A,包含0到N - 1的所有整数。找到最大的集合S并返回其大小,其中 S[i] = {A[i], A[A[i]], A[A[A[i]]], ... }且遵守以下的规则。假设选择索引为i的元素A[i]为S的第一个元素,S的下一个元素应该是A[A[i]],之后是A[A[A[i]]]... 以此类推,不断添加直到S出现重复的元素。

示例 1:
输入: A = [5,4,0,3,1,6,2]
输出: 4
解释:
A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2.
其中一种最长的 S[K]:
S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0}
提示:
N是[1, 20,000]之间的整数。
A中不含有重复的元素。
A中的元素大小在[0, N-1]之间。

思路:说实话,这个题我见过类似的。就是前两周的一次周赛,当时那个题的名字叫做朋友圈啊。其实这个题我觉得还挺简单的。首先确定所有元素都是非负数了。然后从0开始遍历(因为0不能用负数标记,所以要记得0已经用过了)遍历一圈用到的元素变成负数。这些算一个圈子的,记录长度。然后继续遍历第一个非标记的元素。它的圈子也记录比较长度。直到所有的都标记完了,这个时候长度就是我们比较出的最长的了。我去代码实现了
思路没问题,而且性能也挺好的,我直接附上代码:

class Solution {
    public int arrayNesting(int[] nums) {
        int res = 1;//这里为了防止只有一个元素,所以起始就是1
        for(int i = 0;i<nums.length;i++) {
            if(nums[i]<=0)continue;
            int len = 1;
            int temp = nums[i];
            //用过的元素标记为负数
            nums[i] = - nums[i];
            while(temp>0) {
                //当这个值小于等于0.说明已经被用过了
                if(nums[temp]<0) break;
                int cur = temp;
                temp = nums[temp];
                //用过的元素标记为负数
                nums[cur] = -nums[cur];             
                len++;
            }
            res = Math.max(res, len);
        }
        return res;
    }
}

思路其实就是可以互相跳的是一个圈子。最终选择元素最多的圈子就可以了。因为每个元素只会出现一次,所以不会出现交叉的圈子。反正思路就是这样,下一题。

字符串的排列

题目:给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。换句话说,第一个字符串的排列之一是第二个字符串的子串。

示例1:
输入: s1 = "ab" s2 = "eidbaooo"
输出: True
解释: s2 包含 s1 的排列之一 ("ba").
示例2:
输入: s1= "ab" s2 = "eidboaoo"
输出: False
注意:
输入的字符串只包含小写字母
两个字符串的长度都在 [1, 10,000] 之间

思路:这个包含排列,就是s2中有一段子串,是s1中的元素。这个排列之一说的还挺明确的。我读两遍才明白题意。因为两个字符串的长度都在1w以内,所以穷举是不可能了。其次是s1如果长于s2也可以直接false。但是剩下的比对。遇到不存在的直接下一个开始,存在的滑窗。暴力法虽然性能有问题,但是好用啊。我是试试先
额,我居然过了,而且性能超过我的想象,哈哈,直接贴代码:

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int len = s1.length();
        int len2 = s2.length();
        if (len > len2)
            return false;
        int[] arr = new int[26];
        for (char c : s1.toCharArray())
            arr[c - 'a']++;
        int start = 0;
        int sum = 0;
        char[] c2 = s2.toCharArray();
        for (int i = 0; i < len2; i++) {
            char c = c2[i];
            if (arr[c - 'a'] > 0) {//有这个字符,正常往下走
                arr[c - 'a']--;
                sum++;
                if (sum == len)
                    return true;
            } else if (s1.indexOf(c) == -1) {// 没有这个字符判断是次数没了还是单纯没有
                sum = 0;
                for (int j = start; j < i; j++) arr[c2[j] - 'a']++;// 之前怎么减的现在怎么加回来
                start = i + 1;// 从下个开始从头算
            } else {// 说明s1中有这个串,单纯的次数多了,可以从前开始减
                for (int j = start; j < i; j++) {
                    if (c2[j] == c2[i]) {
                        start = j+1;
                        break;
                    }                       
                    sum--;
                    arr[c2[j] - 'a']++;// 之前怎么减的现在怎么加回来
                }
            }
        }
        return false;
    }
}

我是个人感觉我备注写的挺全了,本来以为我写这么墨迹性能不会好呢,哈哈,有点小开心,今天的几道题刷的很顺啊。反正思路就是这样,其实也算是个变相的滑窗了,区别就是遇到s1中没有的字符直接这个窗口里的都不要了,思路不难,代码墨迹,细节点很多,我反正是各种小细节忘了写,然后面向测试案例编程,不断完善的。就不多说了,下一题。

出界的路径数

题目:给定一个 m × n 的网格和一个球。球的起始坐标为 (i,j) ,你可以将球移到相邻的单元格内,或者往上、下、左、右四个方向上移动使球穿过网格边界。但是,你最多可以移动 N 次。找出可以将球移出边界的路径数量。答案可能非常大,返回 结果 mod 109 + 7 的值。

题目截图

思路:这个题怎么说呢,我个人的想法就是递归。周围一圈都是1.说明到了那下一步就可以出去了。其次如果当前大于1步,还可以继续往上下左右走。反正思路有了,我去实现下试试。
哎,其实这个题怎么说呢,在最开始出现了取模的时候我就应该想到这个是个dp的题目。令f(i,j,N)是位于i,j,总共有N步的总数。那么向左走一步就是f(i-1,j,N-1)。这里关键在于可以向四个方向走,所以状态转移方程就是把四个方向上的加起来的总和:

f(i,j,N) = f(i+1,j,N-1)+f(i-1,j,N-1)+f(i,j+1,N-1)+f(i,j-1,N-1);

至此这个题的思路就很清楚了。然後实现代码:

class Solution {
    int m;
    int n;
    int modularity = 1000000007;
    Long[][][] mem;

    public int findPaths(int m, int n, int N, int i, int j) {
        mem = new Long[N + 1][m][n];
        this.m = m;
        this.n = n;
        return (int) (f(N, i, j) % modularity);
    }

    public Long f(int N, int i, int j) {
        if (N < 0) return 0L;
        if (N == 0) {
            if (i < 0 || i >= m || j < 0 || j >= n) return 1L;
            return 0L;
        }
        if (i < 0 || i >= m || j < 0 || j >= n) return 1L;
        if (mem[N][i][j] != null) return mem[N][i][j];
        Long i1 = (f(N - 1, i + 1, j)
                + f(N - 1, i - 1, j)
                + f(N - 1, i, j + 1)
                + f(N - 1, i, j - 1)) % modularity;
        mem[N][i][j] = i1;
        return i1;
    }
}

怎么说呢,这个题说简单也不简单,说难也不算难。只要思路明确了。递归条件找好了就行了,感觉思路还是挺清楚的。就不多说了,下一题。

两个字符串的删除操作

题目:给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。

示例:
输入: "sea", "eat"
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
提示:
给定单词的长度不超过500。
给定单词中的字符只含有小写字母。

思路:这个题应该是找两个字符串的最大子序列吧。然後除了这个子序列的都删除。重点就是这两个最大子序列。但是幸好我以前做过类似的题目。用的是dp。
ac了,就是性能略差。我这里先贴代码:

class Solution {
    public int minDistance(String word1, String word2) {
        int l1 = word1.length();
        int l2 = word2.length();
        //判断w1和w2的最大子序列
        int[][] d = new int[l1+1][l2+1];
        for(int i = 0;i<l1;i++) {
            for(int j = 0;j<l2;j++) {
                //需要注意的是这个d的下标要在i和j的基础上都+1
                if(word1.charAt(i)==word2.charAt(j)) {
                    d[i+1][j+1] = d[i][j]+1;
                }else if(d[i][j+1]>d[i+1][j]) {
                    d[i+1][j+1] = d[i][j+1];
                }else {
                    d[i+1][j+1] = d[i+1][j];
                }
            }
        }
        return l1+l2-(d[l1][l2]*2);
    }
}

这种题目是我喜欢的那种题,稍微有点难度,但是我能做出来。而且中间有思考的过程。反正这个是很标准的一个dp。本质上,如果两个两个字符是相等的,那么这个公共子序列就会多一个,所以是上一个公共子序列的基础上+1.反之如果不相等,要判断两边到i,和到j那个结果最长,选择最长的那个。
性能问题我觉得可能是细节处理没处理好,我去试试换成数组。
没有换数组,简单的换了下遍历的下标(之前的以两次字符串为准,现在是以dp数组下标为准)性能就上来了:


性能超过百分之八十七

所以这个题就这样吧,下一题了。

分数加减运算

题目:给定一个表示分数加减运算表达式的字符串,你需要返回一个字符串形式的计算结果。 这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1。

示例 1:
输入:"-1/2+1/2"
输出: "0/1"
示例 2:
输入:"-1/2+1/2+1/3"
输出: "1/3"
示例 3:
输入:"1/3-1/2"
输出: "-1/6"
示例 4:
输入:"5/3+1/3"
输出: "2/1"
说明:
输入和输出字符串只包含 '0' 到 '9' 的数字,以及 '/', '+' 和 '-'。
输入和输出分数格式均为 ±分子/分母。如果输入的第一个分数或者输出的分数是正数,则 '+' 会被省略掉。
输入只包含合法的最简分数,每个分数的分子与分母的范围是 [1,10]。 如果分母是1,意味着这个分数实际上是一个整数。
输入的分数个数范围是 [1,10]。
最终结果的分子与分母保证是 32 位整数范围内的有效整数。

思路:这个题怎么说呢,我感觉思路是清楚的,就是对字符串的处理上稍显麻烦。首先用1/1作为系数。一个分数一个分数的算就行了。我去试试代码吧。
这个题多次调试后ac了,而且超出我预计的是性能还挺好,我直接贴代码:

class Solution {
    public String fractionAddition(String expression) {
        int fz = 0;
        int fm = 1;
        boolean flag = true;
        char[] c = expression.toCharArray();
        for(int i = 0;i<c.length;i++) {
            if(c[i] == '-') {
                flag = false;
            }else if (c[i] == '+') {
                flag = true;
            }else {//走到这一定是分式了,所以直接解析这个分式
                int z = c[i++]-48;
                while(c[i] != '/') z = z*10+(c[i++]-48);
                i++;//把除号跳过去
                int m = c[i++]-48;
                while(i<c.length && c[i] != '+' && c[i] != '-') m = m*10+(c[i++]-48);
                i--;
                //至此这个表达式的分母和分子都知道了,
                int[] res = count(fz, fm, z, m, flag);
                fz = res[0];
                fm = res[1];            
            }
        }
        if(fz == 0) return "0/1";
        if(fz>0) {
            int y = getValues(fz, fm);
            return (fz/y)+"/"+(fm/y);
        }else {
            fz = -fz;
            int y = getValues(fz, fm);
            return "-"+(fz/y)+"/"+(fm/y);
        }
    }
    public int getValues(int a,int b){
        int small=Math.min(a,b);
        int big=Math.max(a,b);

        if (big%small==0){
            return small;
        }
       return getValues(big%small,small);
    }
    //i1是第一个数分子,i2是第二个数分母。j1是第二个数分子,j2是第二个数分母。flag是true+。false-
    public int[] count(int i1,int i2,int j1,int j2,boolean flag) {
        int b = 0;
        int z = 0;
        for(int k = 1;k<=j2;k++) {//以j2为准,因为j2不会大于10.找出两个分母的最小公倍数
            if((i2*k)%j2==0) {
                b = i2*k;
                break;
            }
        }
        if(flag) {
            z = i1*(b/i2)+j1*(b/j2);
        }else {
            z = i1*(b/i2)-j1*(b/j2);
        }
        return new int[] {z,b};
    }
}

这个代码写的,没啥难的,就是墨迹。一点点把思路用代码写出来就ok了。第一步是分解出每一个公式。然后做加减法。然后最后的值再化简。不过这些用到吗实现还是很麻烦的。反正因为性能不错所以就这样了,我直接去看看性能排行第一的代码:

class Solution {
    public String fractionAddition(String expression) {
        int n = expression.length();
        char[] chars = expression.toCharArray();
        int[] molecule = new int[n], denominator = new int[n];
        Arrays.fill(denominator, 1);
        molecule[0] = Character.isDigit(chars[0]) ? findNumber(chars, 0): 0;
        int p_molecule = 0;
        for (int i = 0; i < n; i++) {
            if (chars[i] == '-') {
                molecule[i + 1] = -1 * findNumber(chars, i + 1);
                p_molecule = i + 1;
            } else if (chars[i] == '+') {
                molecule[i + 1] = findNumber(chars, i + 1);
                p_molecule = i + 1;
            } else if (chars[i] == '/') {
                denominator[p_molecule] = findNumber(chars, i + 1);
            }
        }
        int lcm = denominator[0];
        for (int i : denominator) {
            lcm = lcm(lcm, i);
        }
        int molecule_sum = 0;
        for (int i = 0; i < n; i++) {
            int m = lcm / denominator[i];
            molecule_sum += molecule[i] * m;
        }
        int d = gcd(Math.abs(molecule_sum), lcm);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(molecule_sum / d);
        stringBuilder.append('/');
        stringBuilder.append(lcm / d);
        return stringBuilder.toString();
    }

    private int findNumber(char[] chars, int index) {
        int n = chars.length;
        int res = 0;
        for (int i = index; i < n; i++) {
            if (Character.isDigit(chars[i])) {
                res *= 10;
                res += chars[i] - '0';
            } else break;
        }
        return res;
    }

    private int gcd(int a, int b) {
        return a % b == 0 ? b : gcd(b, a % b);
    }

    private int lcm(int a, int b) {
        return (a * b) / gcd(a, b);
    }
}

相比于我自己的代码,这个的好多处理优雅了点,不过也没觉得特别惊艳,毕竟都是硬生生的计算,唯一算得上算法的也就求两个数的最大公约数和最小公倍数了吧?反正这个题就这样了,下一题。

有效的正方形

题目:给定二维空间中四点的坐标,返回四点是否可以构造一个正方形。一个点的坐标(x,y)由一个有两个整数的整数数组表示。

示例:
输入: p1 = [0,0], p2 = [1,1], p3 = [1,0], p4 = [0,1]
输出: True
注意:
所有输入整数都在 [-10000,10000] 范围内。
一个有效的正方形有四个等长的正长和四个等角(90度角)。
输入点没有顺序。

思路:其实我觉得这个题还好吧,有个简单的思路。任选三个点都是等腰直角三角形就可以了。勾股定理应该就能解决。有个简单的思路,我去代码实现试试。
先贴代码:

public class Solution {
    public boolean validSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
        //任选三个点 都是 一个等腰直角三角形
        return isRightTriangle(p1, p2, p3) && isRightTriangle(p1, p2, p4) && isRightTriangle(p1, p3, p4) && isRightTriangle(p2, p3, p4);
    }

    public boolean isRightTriangle(int[] p1, int[]p2, int[] p3){
        int d1 = (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]);
        int d2 = (p2[0] - p3[0]) * (p2[0] - p3[0]) + (p2[1] - p3[1]) * (p2[1] - p3[1]);
        int d3 = (p3[0] - p1[0]) * (p3[0] - p1[0]) + (p3[1] - p1[1]) * (p3[1] - p1[1]);
        if(d1 > d2 && d2 == d3 && d2 + d3 == d1 ||
            d2 > d1 && d1 == d3 && d1 + d3 == d2 ||
            d3 > d1 && d1 == d2 && d1 + d2 == d3){
            return true;
        }
        return false;
    }
}

这个代码怎么说呢,不复杂, 就是麻烦。我这个思路还算是清晰。但是自己懒得写代码。。。咳咳,反正这个题就这样吧。实现的方式也是多种多样的。我这样判断可以、我看还有的说判断对角线和边。也有的是一个点90度边长旋转。转一圈的。反正就差不多这样。不多说了,下一题。

分割数组为连续子序列

题目:给你一个按升序排序的整数数组 num(可能包含重复数字),请你将它们分割成一个或多个子序列,其中每个子序列都由连续整数组成且长度至少为 3 。如果可以完成上述分割,则返回 true ;否则,返回 false 。

示例 1:
输入: [1,2,3,3,4,5]
输出: True
解释:
你可以分割出这样两个连续子序列 :
1, 2, 3
3, 4, 5
示例 2:
输入: [1,2,3,3,4,4,5,5]
输出: True
解释:
你可以分割出这样两个连续子序列 :
1, 2, 3, 4, 5
3, 4, 5
示例 3:
输入: [1,2,3,4,4,5]
输出: False
提示:
输入的数组长度范围为 [1, 10000]

思路:重点是分成连续的升序子数组。一种是出现某种情况一定会false(比如重复元素数大于总数/3)。还有就是遇到重复元素单出一条线。然后往下遍历满足每一条线,最终有怎么也满足不了的线false。有简单的想法,我去实现下。
这个题怎么说呢,实现了,虽然性能不是很满意,我先贴代码:

class Solution {
    public boolean isPossible(int[] nums) {
        List<List<Integer>> list = new ArrayList<List<Integer>>();
        for(int i = 0;i<nums.length;i++) {
            boolean flag = false;
            //新生成的在最后,所以从后往前追加
            int size = list.size()-1;
            for(int j = size;j>=0;j--) {
                if(isAppend(nums[i], list.get(j)) ) {
                    list.get(j).add(nums[i]);
                    flag = true;
                    break;//这个元素已经用了,直接开始下一个元素
                }else {
                    if(list.get(j).get(list.get(j).size()-1)!=nums[i] && list.get(j).size()<3) {
                        //走到这说明这个线路不能追加当前元素,最后一个不是当前元素且线路不满足3,直接false
                        return false;
                    }else if(list.get(j).get(list.get(j).size()-1)!=nums[i]){
                        list.remove(j);//到这步说明当前线路不能追加了,并且已经超过三个,直接删除
                    }                       
                }
            }
            if(!flag) {//说明这个元素根本没用到,单开一条线路
                List<Integer> list2 = new ArrayList<Integer>();
                list2.add(nums[i]);
                list.add(list2);
            }
        }
        for(List<Integer> i:list) {
            if(i.size()<3) return false;
        }
        return true;
        
    }
    //能不能追加
    public boolean isAppend(int i,List<Integer> list) {
        if(list.get(list.size()-1)+1 != i ) return false;
        return true;
    }
}

不知道为啥最近的题都代码贼多。反正这个题思路是有的。就是能续之前的线就续之前的线。续不了就开新线。而且上面的代码有几个优化点(虽然这么优化性能也不咋地。):一个是如果遇到不符合条件的线直接false。可以少做很多无用功。还有一个大家注意,我这个list是倒叙遍历的,因为后生成的线肯定是较短的(不要抬杠一边长)。我自己最满意的一点就是如果够长了,而且往后断了的线直接remove,这样不用遍历好多无用的线。然后我这么满意的代码,性能一塌糊涂。。。我感觉这个已经不是单纯的思路上的优化能解决的了,可能是list本身就不行。我感觉list可以被替换成二维数组。一个表示当前最后一位数字,一个表示当前长度。我去试试性能会不会提升吧。
我踏马,实现是实现了,性能越来越差了,简直日狗了,不过辛苦写出来的代码不贴出来对不起自己:

class Solution {
    public boolean isPossible(int[] nums) {
        int[][] d = new int[10000][2];//0代表最后一个数字,1代表长度
        d[0] = new int[]{nums[0],1}; 
        int idx = 0;
        for(int i = 1;i<nums.length;i++) {
            boolean flag = false;
            //新生成的在最后,所以从后往前追加
            for(int j = idx;j>=0;j--) {
                if(d[j][0]+1 == nums[i]) {
                    d[j][0]++;
                    d[j][1]++;
                    flag = true;
                    break;      
                }else {
                    if(d[j][0] == nums[i]) continue;
                    //走到这说明是续不上了
                    if(d[j][1]<3) return false;
                }
            }
            if (!flag) d[++idx] = new int[] {nums[i],1};
        }
        for(int i = 0;i<=idx;i++) {
            if(d[i][1]<3) return false;
        }
        return true;
    }
}

只能安慰安慰自己代码量减少了,我去看看性能排行第一的代码吧:
好吧,我先自闭一波,看了人家的题解感觉我就是个废物!首先不说代码,就说思路:
这道题得采用贪心策略,就是使序列尽可能的长。但是这种策略好像给人一种错误的感觉,比如[1,2,3,3,4,5],如果采用此策略,将会是[1,2,3,4,5]和剩余的[3]。其实这个策略并不是这么简单的

  • 比如它扫描到’1’的时候,由于它的前一个元素’0’不存在以’0’结尾的连续子序列,所以它这是向后寻找两个元素,凑成子序列[1,2,3](这时1,2,3各被消耗了一个)。接着我们就应该访问到’3’,我们又去查询以’2’结尾的有没有合法的连续序列,但是没有,所以它此时只能向后寻找两个元素,凑出连续子序列[3,4,5](3,4,5个被消耗了一次),结束访问。

  • 如果输入[1,2,3,3,4,4,5,5],刚开始访问'1',建立[1,2,3],接着访问'3',建立[3,4,5]接着访问'4',由于第一步建立了[1,2,3]以4 - 1结尾的连续子序列,所以它放入,得到[1,2,3,4]接着访问'5',由于第一步建立了[1,2,3,4]以5 - 1结尾的连续子序列,所以它放入,得到[1,2,3,4,5]

这个思路是很清晰明了的,虽然废物如我看了这个还用了好久才写出代码。但是毕竟写出来了,所以记录一下。

class Solution {
    public boolean isPossible(int[] nums) {
        //每个数字出现的次数
        Map<Integer,Integer> countMap = new HashMap<Integer, Integer>(); 
        Map<Integer,Integer> endMap = new HashMap<Integer, Integer>();
        //计数
        for(int i : nums) countMap.put(i, countMap.getOrDefault(i, 0)+1);
        for(int i :nums) {
            if(countMap.get(i)<1) continue;
            countMap.put(i, countMap.get(i)-1);
            int n = endMap.getOrDefault(i-1, 0);
            if(n>0) {//说明前面有子序列可以用,直接追加。                
                endMap.put(i-1, endMap.get(i-1)-1);//以i-1结尾的子序列少一个
                endMap.put(i, endMap.getOrDefault(i, 0)+1);//以i结尾的子序列多一个
            }else {
                //说明没有子序列可用,只能自己开一条
                int i1 = countMap.getOrDefault(i+1, 0);
                int i2 = countMap.getOrDefault(i+2, 0);
                if(i1<1 || i2<1) return false;//说明这个元素凑不齐三个。
                endMap.put(i+2, endMap.getOrDefault(i+2, 0)+1);//以i+2结尾的子序列多了一个
                countMap.put(i+2,countMap.get(i+2)-1);//i+2用了一个
                countMap.put(i+1,countMap.get(i+1)-1);//i+1用了一个
            }
        }
        return true;
    }
}

我脑壳疼,一样一样的思路,人家打败九十多,我打败九多。。。算了算了,我还是直接贴性能第一的代码吧:

class Solution {
    public boolean isPossible(int[] nums) {
        int n = nums.length, i = 0;
        if (n < 3) return false;
        // 当前长度为1,2的序列的数量,当前能用的多于2的序列数量
        int one = 0, two = 0, more = 0;
        // 初始化one
        while (i < n && nums[i] == nums[0]) {
            one++;
            i++;
        }
        while (i < n) {
            int pre = nums[i - 1];
            // 下一个数与上一个数断了的情况,即count为0的特殊情况
            if (nums[i] - pre > 1) {
                // 有one或two则直接返回false
                if (one > 0 || two > 0) {
                    return false;
                }
                // more全部断
                more = 0;
                int start = nums[i];
                // 初始化one
                while (i < n && nums[i] == start) {
                    one++;
                    i++;
                }
            } else {
                int count = 0;
                // 查找比上一个数大1的数的数量
                while (i < n && nums[i] == pre + 1) {
                    count++;
                    i++;
                }
                int remain = count - one - two, temp;
                if (remain < 0) {
                    // 长度1和长度为2的是一定需要被满足的,所以说如果count不够,返回false
                    return false;
                } else {
                    // 满足长度1和2后,贪心算法,如果有more没必要另起一个one,所以remain尽可能给more
                    // 原来的two也变成more,remain如果小于more,那么有一部分more断了不再讨论
                    temp = remain - more;
                    more = Math.min(remain, more) + two;
                }
                // 原来的one变成two
                two = one;
                // 如果remain大于more,则多的另起one,否则没有one
                one = Math.max(0, temp);
            }
        }
        return one + two == 0;
    }
}

这个题简直写的我心痛。反正就这样了吧。解法这么多,我写的没几个好的。
这篇笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注,也祝大家工作顺顺利利,周末愉快!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容