Leetcode热题100

文章目录

  • 前言
  • 热题100
      • 题目
    • 普通数组
      • 53. 最大子数组和
        • 思路:动态规划
    • 子串
      • 560. 和为 K 的子数组
        • 思路:前缀和 +hashmap
          • 前缀和
    • 滑动窗口
      • 算法核心代码
      • 模板代码
      • 3. 无重复字符的最长子串
    • 哈希
      • 1.两数之和
      • 49.字母异位词分组
    • 双指针
      • 11. 盛最多水的容器

前言

记录Leetcode热题100的刷题历程,遇到理解不易的题目我会在题目下方做好详细题解及其解决方案的分析。欢迎关注,一起进步,提高基本功。

热题100

题目

  1. 字符串解码
    给定一个经过编码的字符串,返回它解码后的字符串。
    编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
    你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
    此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1:

输入:s = “3[a]2[bc]”
输出:“aaabcbc”
示例 2:

输入:s = “3[a2[c]]”
输出:“accaccacc”

class Solution{
        public String decodeString(String s) {
            StringBuilder res = new StringBuilder();
            int multi = 0;
            LinkedList<Integer> stackMutil = new LinkedList<>();
            LinkedList<String> stackRes = new LinkedList<>();
            for (Character c :
                    s.toCharArray()) {
                // char 为数字时,将数字字符转化为数字 multi,用于后续倍数计算;
                if (c >= '0' && c <= '9'){
                    System.out.println(" ============= c为数字");
                    multi =  multi * 10 + Integer.parseInt(c + "");
                    System.out.println("multi = " + multi);
                }else if (c == '['){//当 c 为 [ 时,将当前 multi 和 res 入栈,并分别置空置
                    System.out.println(" ============= c 为 [");
                    stackRes.addLast(res.toString());
                    stackMutil.addLast(multi);
                    System.out.println("stackRes = " + Arrays.toString(stackRes.toArray()));
                    System.out.println("stackMutil = " + Arrays.toString(stackMutil.toArray()));
                    //清空
                    multi = 0;
                    res = new StringBuilder();
                }else if (c == ']'){//当 c 为 ] 时,stack 出栈,拼接字符串 res = last_res + cur_multi * res
                    System.out.println(" ============= c 为 ]");
                    Integer mutil = stackMutil.removeLast();
                    StringBuilder tempStr = new StringBuilder();
                    for (int i = 0; i < mutil; i++) {
                        tempStr.append(res);
                    }
                    System.out.println("tempStr = " + tempStr);
                    res = new StringBuilder(stackRes.removeLast() + tempStr);
                    System.out.println("res = " + res.toString());
                }else {
                    System.out.println(" ============= c 为字符");
                    res.append(c);
                    System.out.println("res = " + res.toString());
                }
            }
            return res.toString();
        }
    }

输入:

"3[a2[c]]"

输出:

multi = 3
 ============= c 为 [
stackRes = []
stackMutil = [3]
 ============= c 为字符
res = a
 ============= c为数字
multi = 2
 ============= c 为 [
stackRes = [, a]
stackMutil = [3, 2]
 ============= c 为字符
res = c
 ============= c 为 ]
tempStr = cc
res = acc
 ============= c 为 ]
tempStr = accaccacc
res = accaccacc

普通数组

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

思路:动态规划
class Solution {
        public int maxSubArray(int[] nums) {
            if (nums.length == 0) return 0;
            int n = nums.length;

            //以 nums[i] 为结尾的「最大子数组和」为 dp[i]。
            int[] dp = new int[n];
            dp[0] = nums[0];
            for (int i = 1/*注意,不是以i = 0开头*/; i < n; i++) {
                //最大值有两个选择,nums[i],或者 nums[i] + 前面的最大子数组和
                dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
            }

            //寻找最大值
            int res = Integer.MIN_VALUE;
            for (int i = 0; i < n; i++) {
                res = Math.max(res, dp[i]);
            }
            return res;
        }
    }

子串

560. 和为 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。子数组是数组中元素的连续非空序列。 示例 1:输入:nums = [1,1,1], k = 2
输出:2


class Solution {
        public int subarraySum(int[] nums, int k) {
            int count = 0;
            int prefix = 0;
            HashMap<Integer, Integer> hashMap = new HashMap<>();
            hashMap.put(0, 1);//key = 前缀和, value = 前缀和的出现的次数
            for (int i = 0; i < nums.length; i++) {
                prefix += nums[i];
                //检查哈希表中是否存在键prefix - k,如果存在,说明之前的前缀和中有某个子数组的和等于k - prefix,这意味着从那个子数组到当前
                //位置的子数组和为k。因此,将count增加哈希表中键为pre - k的值。
                if (hashMap.containsKey(prefix - k)){
                    count += hashMap.get(prefix - k);
                }
                //存储每个prefix及其数量到hashmap中
                hashMap.put(prefix,hashMap.getOrDefault(prefix,0) + 1);
            }
            return count;
        }
    }

思路:前缀和 +hashmap
前缀和

前缀和的优势:以(o1)的时间复杂度得到某块区间的总和。

前缀和(Prefix Sum)是一种在计算中经常使用的技巧,用于有效地计算数组中某一范围内元素的和。前缀和的基本思想是创建一个新的数组,其中每个元素表示原始数组中前若干个元素的累加和。

假设有一个数组 arr,前缀和数组 prefixSum 的第 i 个元素 prefixSum[i] 表示原数组 arr 中前 i 个元素的和。具体而言,prefixSum[i] 等于 arr[0] + arr[1] + ... + arr[i]

使用前缀和的好处是,在计算任意区间 [left, right] 内元素的和时,你只需要执行一次减法操作,即 prefixSum[right] - prefixSum[left - 1](注意要处理 left 为0的情况)。这比遍历区间内的元素并逐个相加要高效得多,尤其在需要多次查询不同区间和的情况下。

前缀和在处理数组或序列的部分和问题(如子数组和、区间和、累积和)非常有用,常见的应用包括解决子数组和等于特定值的问题,以及在一维和二维数组中进行区间和的快速查询。

/**
 * @author linshujie
 */
public class PrefixSum {
    public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4, 5 };
        int n = arr.length;
        prefixSum(arr, n);
    }

    private static void prefixSum(int[] arr, int n) {
        // 创建一个数组来存储前缀和
        int[] prefixSum = new int[n];

        // 计算前缀和并存储在prefixSum数组中
        prefixSum[0] = arr[0];
        for (int i = 1; i < n; i++) {
            prefixSum[i] = prefixSum[i - 1] + arr[i];
        }

        // 输出前缀和数组
        System.out.println("Prefix Sum Array:");
        for (int i = 0; i < n; i++) {
            System.out.print(prefixSum[i] + " ");
        }
    }
}

滑动窗口

算法核心代码

int left = 0, right = 0;

while (left < right && right < s.size()) {
    // 增大窗口
    window.add(s[right]);
    right++;
    
    while (window needs shrink) {
        // 缩小窗口
        window.remove(s[left]);
        left++;
    }
}

模板代码

class Solution {
        public int slideWindow(String s) {
        
        //选择合适数据结构作为window
            Map<Character, Integer> window = new HashMap<>();
            int left = 0, right = 0, res = 0;
            while (right < s.length()) {//右边界
                //扩大窗口
                char cRight = s.charAt(right);
                hashMap.put(cRight, hashMap.getOrDefault(cRight, 0) + 1);//记录c的数量
                right++;
                
                //打印,查看算法执行情况,便于调整细节
                // System.out.println("left = " + left + " right = " + right);
                
                //缩小窗口
                while (/*满足条件*/) {
                    char cLeft = s.charAt(left);
                    hashMap.put(cLeft,hashMap.get(cLeft) - 1);
                    left++;
                }
                
                //更新窗口最新数据
               ...
            }
            return res;
        }
    }

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1:输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。


class Solution {
        public int lengthOfLongestSubstring(String s) {
            Map<Character, Integer> hashMap = new HashMap<>();
            int left = 0, right = 0, res = 0;
            while (right < s.length()) {
                //扩大窗口
                char cRight = s.charAt(right);
                hashMap.put(cRight, hashMap.getOrDefault(cRight, 0) + 1);//记录c的数量
                right++;
                // System.out.println("left = " + left + " right = " + right);
                //缩小窗口
                while (/*满足条件*/hashMap.get(cRight) > 1) {
                    char cLeft = s.charAt(left);
                    hashMap.put(cLeft,hashMap.get(cLeft) - 1);
                    left++;
                }
                res = Math.max(res, right - left);
            }
            return res;
        }
    }

哈希

1.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。

示例 1:输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

static class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> hashMap = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (hashMap.containsKey(target - nums[i])){
return new int[]{hashMap.get(target - nums[i]),i};
}
hashMap.put(nums[i],i);
}
return new int[]{};
}
}

49.字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例 1:输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]输出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]]


class Solution {
        public List<List<String>> groupAnagrams(String[] strs) {
            //新建map
            Map<String,List<String>> hashmap = new HashMap<>();
           
            for (String str :
                    strs) {
                //获取key
                char[] chars = str.toCharArray();
                Arrays.sort(chars);
                String key = new String(chars);
                //添加到list
                List<String> list = hashmap.getOrDefault(key, new ArrayList<>());
                list.add(str);
                //添加到map
                hashmap.put(key,list);
            }
            return new ArrayList<>(hashmap.values());
        }
    }


双指针

11. 盛最多水的容器

class Solution {
        public int maxArea(int[] height) {
            int left = 0,right = height.length - 1;
            int res = 0;
            while (left< right){
                int area = (right - left) * Math.min(height[left],height[right]);
                res = Math.max(res,area);
                if (height[left] < height[right]){//最大面积取决于短板,移动短板
                    left ++;
                }else {
                    right --;
                }
            }
            return res;
        }
    }

你可能感兴趣的:(leetcode,哈希算法)