刷leetCode算法题+解析(四十九)

最后一块石头的重量

题目:有一堆石头,每块石头的重量都是正整数。每一回合,从中选出两块最重的石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

提示:

1 <= stones.length <= 30
1 <= stones[i] <= 1000

思路:这道题的思路怎么说呢,就是最大值两两相减,然后差值不是0就存数组重排序,继续找两个最大值。感觉应该比较麻烦但是不难。
emmmmm.....确实是不难,但是我一度在做的时候怀疑自己做错了,毕竟真的很麻烦,不断在新建数组什么的,但是运行性能超过百分之九十八,,所以贴代码下一题:

class Solution {
    public int lastStoneWeight(int[] stones) {
        if(stones.length==1) return stones[0];
        int[] res = getWeight(stones);
        while(res.length>1){
            res = getWeight(res);
        }
        return res.length==0?0:res[0];
    }
    public int[] getWeight(int[] stones){
        int index = -1;
        int index1 = -1;
        int max = 0;
        int max1 = 0;
        for(int i = 0;i<stones.length;i++){
            if(stones[i]>=max){
                max1 = max;
                max = stones[i];
                index1 = index;
                index = i;
            }else if(stones[i]>max1){
                max1 = stones[i];
                index1 = i;
            }            
        }
        boolean flag = max1==max;
        int[] res  = new int[flag?stones.length-2:stones.length-1];
        int x = 0;
        for(int i = 0;i<stones.length;i++){
            if(i!=index && i!=index1){
                res[x] = stones[i];
                x++;
            }
            
        }
        if(!flag) res[res.length-1] = max-max1;
        return res;
    }
}

删除字符串中所有相邻重复项

题目:给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:
输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
提示:

1 <= S.length <= 20000
S 仅由小写英文字母组成。

思路:这道题第一反应递归,直到遍历一次长度不变是递归出口。然后怎么判断就很多样了,刚刚用测试案例测了一下,三个连在一起也只删除两个。这个题做出来很容易,做的优雅很难。我先去实现了。
好的吧,我选择了最不优雅的一种方式:

class Solution {
    String s;
    public String removeDuplicates(String S) {
        s = S;
        int res = removeD();
        while(removeD() != res){
            res = removeD();
        }
        return s;
    }
    public int removeD(){
       char[] c = s.toCharArray();
       for(int i = 0;i<c.length-1;i++){
           if(c[i]==c[i+1]){
               c[i] = ' ';
               c[i+1] = ' ';
               i++;
           }
       }
       s = new String(c).replace(" ","");
       return s.length();
    }
}

但是我决定用更不优雅的一种方式实现:

class Solution {
    String s;
    public String removeDuplicates(String S) {
        s = S;
        int res = removeD();
        while(removeD() != res){
            res = removeD();
        }
        return s;
    }
    public int removeD(){
       s = s.replace("aa","");
       s = s.replace("bb","");
       s = s.replace("cc","");
       s = s.replace("dd","");
       s = s.replace("ee","");
       s = s.replace("ff","");
       s = s.replace("gg","");
       s = s.replace("hh","");
       s = s.replace("ii","");
       s = s.replace("jj","");
       s = s.replace("kk","");
       s = s.replace("ll","");
       s = s.replace("mm","");
       s = s.replace("nn","");
       s = s.replace("oo","");
       s = s.replace("pp","");
       s = s.replace("qq","");
       s = s.replace("rr","");
       s = s.replace("ss","");
       s = s.replace("tt","");
       s = s.replace("uu","");
       s = s.replace("vv","");
       s = s.replace("ww","");
       s = s.replace("xx","");
       s = s.replace("yy","");
       s = s.replace("zz","");
       return s.length();
    }
}

对的,就这么简单粗暴也没超时。好了好了,心态平和一点了,之前我以为拆分成char肯定性能不错,但是没想到只超过了百分之七十的人,现在看看还是哪里没做好,非要说的话我觉得败笔就是replace那块了,我尝试改进一下。
emmm...好了,性能百分百了,这个string的valueOf一个char,然后确定起始下标和结束下标是刚刚看源码看的,学到一个api而且性能到了百分百,开心,贴最终版的代码:

class Solution {
    String s;
    public String removeDuplicates(String S) {
        s = S;
        int res = removeD();
        while(removeD() != res){
            res = removeD();
        }
        return s;
    }
    public int removeD(){
       char[] c = s.toCharArray();
       char[] res =  new char[c.length];
       int index = 0;
       for(int i = 0;i<c.length;i++){
           if(i<c.length-1 && c[i]==c[i+1]){
               i++;               
               continue;
           }
           res[index] = c[i];
           index++;
       }
       s = String.valueOf(res,0,index);
       return s.length();
    }
}

高度检查器

题目:学校在拍年度纪念照时,一般要求学生按照 非递减 的高度顺序排列。请你返回至少有多少个学生没有站在正确位置数量。该人数指的是:能让所有学生以 非递减 高度排列的必要移动人数。

示例:
输入:[1,1,4,2,1,3]
输出:3
解释:
高度为 4、3 和最后一个 1 的学生,没有站在正确的位置。
提示:

1 <= heights.length <= 100
1 <= heights[i] <= 100

思路:这道题感觉是以位置主导的。比如除了第一个剩下的都是升序排列,但是因为第一个位置不对,则第一个元素前面的所有人都是错了的。比如 712345、这个错误的就是所有人,因为7站的靠前了,所有人位置都往后了。感觉一个方法就是升序排序,所以下标不是规定值的就res++。我先去实现下,再说自己实现排序的事。
好的,思路没问题,性能也ok,超过百分之九十八的人,一次及格。

class Solution {
    public int heightChecker(int[] heights) {
        int res = 0;
        int[] h1 = new int[heights.length];
        int idx = 0;
        for(int i :heights){
            h1[idx] = i;
            idx++;
        }
        Arrays.sort(heights);
        for(int i = 0;i<h1.length;i++){
            if(h1[i]!=heights[i]) res++;
        }
        return res;
    }
}

既然这道题这么简单,那就不多浪费时间了,继续下一题。。

字符串的最大公因子

题目:对于字符串 S 和 T,只有在 S = T + ... + T(T 与自身连接 1 次或多次)时,我们才认定 “T 能除尽 S”。返回字符串 X,要求满足 X 能除尽 str1 且 X 能除尽 str2。

示例 1:
输入:str1 = "ABCABC", str2 = "ABC"
输出:"ABC"
示例 2:
输入:str1 = "ABABAB", str2 = "ABAB"
输出:"AB"
示例 3:
输入:str1 = "LEET", str2 = "CODE"
输出:""
提示:

1 <= str1.length <= 1000
1 <= str2.length <= 1000
str1[i] 和 str2[i] 为大写英文字母

思路:这种题做过类似的,首先要判断的是str1和str2是不是包含关系(短的是长的中的子串),不是的话没办法同时满足两个都除尽。其次就是判重了,我目前的想法反正就是26个英文字母。用数组代替下标查看能构成一组子串的长度,然后判断这个是不是子串。就是这样,可能我表达的不是很清楚,但是思路我觉得还是很靠谱的,我去实现了。
好的吧,做了半天也就这样了,一共写了两个版本,都是1ms,超过百分之九十五的性能。都贴出来分享下:

class Solution {
    public String gcdOfStrings(String str1, String str2) {
        int l1 = str1.length();
        int l2 = str2.length();
        if(str1.indexOf(str2)==-1 && str2.indexOf(str1)==-1) return "";
        if(str1.equals(str2)) return str1;
        int end = maxGcd(Math.abs(l1-l2),l1>l2?l2:l1);  
        StringBuffer sb = new StringBuffer();
        String temp = str1.substring(0,end);
        for(int i = 0; i<l1/end;i++){
            sb.append(temp);
        }
        if(!sb.toString().equals(str1)) return "";
        StringBuffer sb1 = new StringBuffer();
        for(int i = 0; i<l2/end;i++){
            sb1.append(temp);
        }
        if(!sb1.toString().equals(str2)) return "";
        return temp;
        
    }
    public int maxGcd(int x ,int y){
        for(int i=(x>y?y:x);i>=1;i--){
            if(x%i==0 && y%i==0) return i;
        }
        return -1;
    }
}

其实这个有个思路,就是因为两个串都要是重叠子串组成的。所以肯定两个串从0开始要一样。直到短的那个没了。然后短的那个从头比较还是要一样,不断重叠,直到长的也没了。
我可能说的不清楚,我画个图、反正就是什么时候比较元素不一样了说明不是重叠字符串,直接返回空串就行了。


image.png

首先要确定是不是重叠子串,其次判断最小公因子。这个数应该是较短数组的长度(最大公因子)和长度差的最大公因数。
说下原理,首先这个最大公因子最大也就是较短数组的长度,因为再长点这个较短字符串都不包含了。所以不可能。其次就是长度差,这个如果长度差是1,则说明这个1也是满足重叠子串的,所以这个重叠子串只能是1.
再之后如果长度差是3,而较短串长4.我们这么想:如果重叠串是3,那么较短串不可能满足,同样重叠串4余数不满足。所以只能选择两个数的最大公因数。
最后一步就是根据最大公因数再把这个串拼起来,看是不是之前的str1和str2,不是说明没满足这个规律,直接空串返回。
两个都是说明没找错,可以返回了。
这个题就是表述起来比较墨迹,其实做起来没那么难,直接贴代码:

class Solution {
    public String gcdOfStrings(String str1, String str2) {
        int l1 = str1.length();
        int l2 = str2.length();
        if(str1.indexOf(str2)==-1 && str2.indexOf(str1)==-1) return "";
        if(str1.equals(str2)) return str1;
        int end = maxGcd(Math.abs(l1-l2),l1>l2?l2:l1);  
        StringBuffer sb = new StringBuffer();
        String temp = str1.substring(0,end);
        for(int i = 0; i<l1/end;i++){
            sb.append(temp);
        }
        if(!sb.toString().equals(str1)) return "";
        StringBuffer sb1 = new StringBuffer();
        for(int i = 0; i<l2/end;i++){
            sb1.append(temp);
        }
        if(!sb1.toString().equals(str2)) return "";
        return temp;
        
    }
    public int maxGcd(int x ,int y){
        for(int i=(x>y?y:x);i>=1;i--){
            if(x%i==0 && y%i==0) return i;
        }
        return -1;
    }
}

如上代码,这个没我写的那么墨迹,因为判断是不是重叠串组成的部分我给割了。第一遍提交性能没到百分百我觉得是不是来回来去比较耗性能,所以改成就现在这个版本,事实证明还是没到百分百。所以。。我又懒得改回来。
算了,我直接去看排行第一的代码吧:
这个代码处理的很优雅啊~~豁然开朗恍然大悟,我直接贴出来:

class Solution {
    public static String gcdOfStrings(String str1, String str2) {

        int lenStr1 = str1.length();
        int lenStr2 = str2.length();
        int rem = gcb(lenStr1, lenStr2);
        String str11 = str1.concat(str2);
        String str22 = str2.concat(str1);
        if (!str11.equals(str22)) {
            return "";
        } else {
            return str1.substring(0, rem);
        }
    }

    //最大公因数
    public static int gcb(int a, int b) {
        int rem = 0;
        while (b != 0) {
            rem = a % b;
            a = b;
            b = rem;
        }
        return a;
    }
}

这里用到的concat是追加的意思(我反正这么理解的)。str1 + str2 == str2+str1 说明这两个字符串是重叠子串组成的,只不过重叠次数不一样相同而已。
然后确定这点就找两个数的最大公因数。而不是我想的那个余数和短数的最大公因数,这块是我想复杂的。最后返回就ok了。简直是。。。别人家的脑子怎么长的呢?

Bigram 分词

题目:给出第一个词 first 和第二个词 second,考虑在某些文本 text 中可能以 "first second third" 形式出现的情况,其中 second 紧随 first 出现,third 紧随 second 出现。对于每种这样的情况,将第三个词 "third" 添加到答案中,并返回答案。

示例 1:
输入:text = "alice is a good girl she is a good student", first = "a", second = "good"
输出:["girl","student"]
示例 2:
输入:text = "we will we will rock you", first = "we", second = "will"
输出:["we","rock"]
提示:

1 <= text.length <= 1000
text 由一些用空格分隔的单词组成,每个单词都由小写英文字母组成
1 <= first.length, second.length <= 10
first 和 second 由小写英文字母组成

思路:这个题我不知道是不是我有点没理解。是不是可以说用空格分隔单词数组,当遇到first的单词以后,选择下下一个单词,遇到second单词以后选择下一个单词就ok了?我去实现下看看。
很好,我理解错了,并且还没明白题意。。。怎么就两个数能有三个结果呢?什么鬼?我再去审题吧。

image.png

好的吧,看了N此题终于看懂了,我果然是整个都理解偏了,这个题是第一个单词是first第二个单词是second才会输出第三个单词。。。更简单了有没有。。。我去实现了。

class Solution {
    public String[] findOcurrences(String text, String first, String second) {
        String[] a = text.split(" ");
        String[] b = new String[a.length];
        int n = 0;
        for(int i = 0;i<a.length-2;i++){
            if(a[i].equals(first)&& a[i+1].equals(second)){
                b[n] = a[i+2];
                n++;
            }
        }
        String[] res = new String[n];
        for(int i = 0;i<n;i++){
            res[i] = b[i];
        }
        return res;
    }        
}

这道题其实真的很简单,就怪我第一次审题没审明白。好了,不多说了,直接过了。

复写0

题目:给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。

注意:请不要在超过该数组长度的位置写入元素。要求:请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

示例 1:
输入:[1,0,2,3,0,4,5,0]
输出:null
解释:调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]
示例 2:
输入:[1,2,3]
输出:null
解释:调用函数后,输入的数组将被修改为:[1,2,3]
提示:
1 <= arr.length <= 10000
0 <= arr[i] <= 9

思路:虽然因为上一题的审题不清让我自己多废了点事,但是这道题我还是很自信的把思路理出来了。我这里的想法就是第一遍遍历数组。判断有几个0.第二遍则是根据0数相应的从后往前填充元素值。我先去实现试试。
好了,实现了,中间各种debug还有修修改改,终于是完事了。这道题不难,但是可能我现在思路比较乱,居然硬生生推翻重做好几次!!
就是思路是有的,但是因为细节没过,我室友还在打王者,真真的日了狗了。。。
好了,直接贴代码:

class Solution {
    public void duplicateZeros(int[] arr) {
        int n = 0;
        int idx = -1;
        boolean flag = false;
        for(int i = 0;i<arr.length;i++){
            if(arr[i]==0) n++;
            if(i+n==arr.length-1){
                idx = i;
                flag = true;
                break;
            }else if(i+n>arr.length-1){
                idx = i;
                break;
            }
        }
        int j = arr.length-1; 
        arr[j] = arr[idx];
        j--;
        if(arr[idx]==0 && flag) {
            arr[j] = 0;
            j--;
        }
        for(int k = j;k>=0;k--){
            idx--;
            arr[k] = arr[idx];
            if(arr[idx]==0){
                k--;
                if(k>0) arr[k] = 0;
            }            
        }       
    }
}

首先思路是我之前的思路,就是先看在范围内有几个0 .找到最后一个存储坐标的点。但是有种情况 就是最后一个是0,而且不复制因为没位置,所以要单独提出来处理。
剩下的就没啥了。
一个idx是复制后存在的元素的下标。最后循环的k是当前元素的下标, 我一开始做这两个还弄混了。反正做的乱七八糟的。还是我心不静吧。

分糖果2

题目:排排坐,分糖果。我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友。给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果。然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,依此类推,直到给最后一个小朋友 2 * n 颗糖果。重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果。注意,就算我们手中的剩下糖果数不够(不比前一次发出的糖果多),这些糖果也会全部发给当前的小朋友。返回一个长度为 num_people、元素之和为 candies 的数组,以表示糖果的最终分发情况(即 ans[i] 表示第 i 个小朋友分到的糖果数)。

示例 1:
输入:candies = 7, num_people = 4
输出:[1,2,3,1]
解释:
第一次,ans[0] += 1,数组变为 [1,0,0,0]。
第二次,ans[1] += 2,数组变为 [1,2,0,0]。
第三次,ans[2] += 3,数组变为 [1,2,3,0]。
第四次,ans[3] += 1(因为此时只剩下 1 颗糖果),最终数组变为 [1,2,3,1]。
示例 2:
输入:candies = 10, num_people = 3
输出:[5,2,3]
解释:
第一次,ans[0] += 1,数组变为 [1,0,0]。
第二次,ans[1] += 2,数组变为 [1,2,0]。
第三次,ans[2] += 3,数组变为 [1,2,3]。
第四次,ans[0] += 4,最终数组变为 [5,2,3]。
提示:
1 <= candies <= 10^9
1 <= num_people <= 1000

思路:这道题比较简单,就是一圈一圈的分被,直到糖果不够就是出口。感觉比较简单,我直接去写代码。
一次搞定,而且性能超过百分之九十三,也算是及格了,因为题目简单也不多说什么了,直接贴上代码:

class Solution {
    public int[] distributeCandies(int candies, int num_people) {
        int[] res = new int[num_people];        
        int n = 1;
        while (candies>0){            
            for(int i = 0;i<res.length;i++){
                if(candies>n){
                    res[i] +=n;
                    candies -= n; 
                    n++;
                }else{
                    res[i] += candies;
                    return res;
                }

            }          
        }
        return res;
        
    }
}

今天的笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注~~~也祝大家工作顺顺利利,周末愉快!

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

推荐阅读更多精彩内容