新手的算法记录打卡

42 阅读6分钟

45. 跳跃游戏 II思路:从左走到右边的一座桥需要创建,因为给定的条件是一定会有到右边的桥所有不用判断不会成立的一个情况,然后我们就创建两个变量,一个是目前达到的最远点,一个是接下来可以到达的最远点,如果现在的这个位置等于已经走到的最远的桥了,那么我们就必须要去建桥。也就是从第一步开始走然后贪心的选择可以达到的最远的一直走到结尾结束。贪心感觉就是按着目前可以走到的最远处一直走只走最大值。

class Solution {
    public int jump(int[] nums) {
        int ans = 0;
        int curRight = 0; // 已建造的桥的右端点
        int nextRight = 0; // 下一座桥的右端点的最大值
        for (int i = 0; i < nums.length - 1; i++) {
            // 遍历的过程中,记录下一座桥的最远点
            nextRight = Math.max(nextRight, i + nums[i]);
            if (i == curRight) { // 无路可走,必须建桥
                curRight = nextRight; // 建桥后,最远可以到达 next_right
                ans++;
            }
        }
        return ans;
    }
}

274. H 指数思路:首先我们先排序,因为排序了然后我们就可以获取长度然后进入到循环当中,因为是升序排列的,所以从后面开始循环因为要求是要求出最大的一个H指数。然后这个currH是用来记录当前这个位置到后面的个数因为是升序的所以这个currH是记录着,大于当前citations[i]的个数,如果citations[i]大于这个个数就满足了我们需要的这个条件,然后就更新这个h我们需要的条件。然后这个currH是从1开始循环找的所以当小于等于的时候后面的例子就会都不成立了。

版本1.因为找到最高的h后这个else的break其实可以优化,但如果数据量过多的话,是可以加break增加代码运行效率。

class Solution {
  public int hIndex(int[] citations) {
        Arrays.sort(citations); // 升序排序
        int n = citations.length;
        int h = 0;
        for (int i = n - 1; i >= 0; i--) {
            int currH = n - i; // 当前可能的 h 值
            if (citations[i] >= currH) {
                h = currH; // 更新 h
            } else {
                break; // 后面的 citations[i] 更小,无需继续
            }
        }
        return h;
    }
}

版本2.

class Solution {
  public int hIndex(int[] citations) {
        Arrays.sort(citations); // 升序排序
        int n = citations.length;
        int h = 0;
        for (int i = n - 1; i >= 0; i--) {
            int currH = n - i;
            if (citations[i] >= currH) {
                h = currH; // 更新 h
            }
        }
        return h;
    }
}

1695. 删除子数组的最大得分思路:滑动窗口的一个简答思路,因为是需要连续的且里面没有重复元素,就使用滑动窗口往右扩展,直到出现了重复的时候扩展停止,然后左边窗口开始闭合直到没有重复元素的情况下才可以继续扩展右边窗口,然后找到一个最大值就可以了。

class Solution {
    public int maximumUniqueSubarray(int[] nums) {
        int result = 0;
        HashSet<Integer> set = new HashSet<>();
        int left = 0;
        int count = 0;
        for (int right = 0; right < nums.length; right++) {
            while (set.contains(nums[right])) {
                count -= nums[left];
                set.remove(nums[left]);
                left++;
            }
            set.add(nums[right]);
            count += nums[right];
            result = Math.max(count, result);
        }
        return result;
    }
}

1208. 尽可能使字符串相等思路:里面讲到了一个很好的双指针思路,双指针其实就是枚举左边界扩展右边界当达到一个A[L,R]满足了题目需要的条件但是A[L,R+1]不满足题目的条件使得我们得到了一个极大或极小的一个答案就可以得到了。那么这题的思路其实就是一个不定长的一个滑动窗口,而且只能等位替换题目上写了,我一开始多想了我以为是要不等位对换想复杂了,是等位对换的话,那么就可以先计算出两个字符串等位的差值然后求这个差值和小于maxCost的最长窗口就可以了,然后当中有一个问题就是为什么循环是到n,我一开始写的n-1习惯了长度导致后面测试的时候报错了。

class Solution {
  public int equalSubstring(String s, String t, int maxCost) {
        int n = s.length();
        int[] diff = new int[n];
        for (int i = 0; i < n; i++) {
            diff[i] = Math.abs(s.charAt(i) - t.charAt(i));
        }
        int left = 0, right = 0, sum = 0;
        int ans = 0;
        for (right = 0; right < n; right++) {
            sum += diff[right];
            while (sum > maxCost) {
                sum -= diff[left];
                left++;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}

904. 水果成篮思路:也是跟上面的一样不定长滑动窗口,需要获取能获得最多果树的长度,一次只能拿两种不同的果树,那么我们就需要建立一个hashmap里面只能有两种数据,如果超过两种就收缩左窗口减去里面的数据直到没有两种数据,如果没有两种数据就一直扩展右窗口取最大的结果就可以了,但是这个效率太慢了运行时间只有超过了5%,再想想怎么优化。

class Solution {
  public int totalFruit(int[] fruits) {
        int ans = 0;
        HashMap<Integer, Integer> map = new HashMap<>();
        int left = 0, right;
        for(right=0;right<fruits.length;right++){
            if(!map.containsKey(fruits[right])){
                map.put(fruits[right], 1);
            }else {
                map.put(fruits[right], map.get(fruits[right]) + 1);
            }
            while(map.size()>2){
                map.put(fruits[left], map.get(fruits[left]) - 1);
                if(map.get(fruits[left])<=0){
                    map.remove(fruits[left]);
                }
                left++;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}

优化代码:使用了hashmap里面的merge方法优化了if-else逻辑

class Solution {
    public int totalFruit(int[] fruits) {
        Map<Integer, Integer> map = new HashMap<>();
        int left = 0, ans = 0;
        for (int right = 0; right < fruits.length; right++) {
            map.merge(fruits[right], 1, Integer::sum); // 等价于 map.put(fruits[right], count + 1)
            while (map.size() > 2) {
                map.merge(fruits[left], -1, Integer::sum); // 减少计数
                if (map.get(fruits[left]) == 0) {
                    map.remove(fruits[left]); // 归零时删除
                }
                left++;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}

2024. 考试的最大困扰度思路:脑海里第一时间是想到两个循环一个循环T一个循环F然后分别求最长的,最后求得最终的结果,但是感觉是有点麻烦,思考一下如何优化。这是我的屎山代码。

class Solution {
    public int maxConsecutiveAnswers(String answerKey, int k) {
        int ans = 0;
        int left = 0, right = 0, sum = 0;
        for (right = 0; right < answerKey.length(); right++) {
           if (answerKey.charAt(right)=='T'){
               sum ++;
           }
           while(sum > k){
               if (answerKey.charAt(left)=='T'){
                   sum--;
               }
               left++;
           }
           ans = Math.max(ans, right - left + 1);
        }
        left =0;
        sum = 0;
        for (right = 0; right < answerKey.length(); right++) {
            if (answerKey.charAt(right)=='F'){
                sum ++; 
            }
            while(sum > k){
                if (answerKey.charAt(left)=='F'){
                    sum--;
                }
                left++;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}

看了灵神的思路:代码实现时,由于 T 和 F 的 ASCII 值除以 2 后的奇偶性不同,首先处理字符串成一个数组,然后使用这个cnt来存储字串右移一位模一的数值,因为T和F的特殊性两个的这个数值计算后只会是0和1,那么开始向右边扩展窗口,如果出现了两个的数量都大于k的情况那么就会出现反转任意一个都会大于k的情况所以需要循环到只有一个大于k的情况然后求窗口的最长值。

class Solution {
    public int maxConsecutiveAnswers(String answerKey, int k) {
        char[] s = answerKey.toCharArray();
        int ans = 0;
        int left = 0;
        int[] cnt = new int[2];
        for (int right = 0; right < s.length; right++) {
            cnt[s[right] >> 1 & 1]++;
            while (cnt[0] > k && cnt[1] > k) {
                cnt[s[left] >> 1 & 1]--;
                left++;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}

2779. 数组的最大美丽值思路:因为可以加减k,就是需要先排序然后扩展窗口,如果这个窗口可以满足最大的减最小的在2k的范围内那么这个窗口里面的数就都可以实现成为同一个数,如果不满足就往左边缩小窗口直到循环结束找出最大的窗口。

class Solution {
    public int maximumBeauty(int[] nums, int k) {
        Arrays.sort(nums);
        int ans = 0;
        int left = 0;
        for (int right = 0; right < nums.length; right++) {
            while (nums[right] - nums[left] > k * 2) {
                left++;
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}