力扣Hot100(Java版本)

写在前文:由于LeetCode100的答案多种多样,本文针对每一题只写一种解法(尽可能简单易懂)。针对不容易理解的,采用图片形式。

1. 哈希

1.1 两数之和

题目描述:

  • 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

  • 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

  • 你可以按任意顺序返回答案。

力扣链接:

https://leetcode.cn/problems/two-sum/description/【简单】

解题思路:

  1. 实例化一个HashMap来保存<值, 索引>

  2. 遍历HashMap,找到就返回索引下标,找不到就添加元素

核心代码:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        // map保存<值,索引>
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0; i < nums.length; ++i){
            if(map.containsKey(target - nums[i])){
                return new int[]{map.get(target - nums[i]),i};
            }
            map.put(nums[i],i); // 返回[索引1,索引2]
        }
        return new int[0]; // 返回[]
    }
}

1.2 字母异位词分组

题目描述:

  • 给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

  • 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

力扣链接:

https://leetcode.cn/problems/group-anagrams/description【中等】

解题思路:

  1. Map保存<排序后的字符串, List<String>>
  2. 遍历strs, 依次添加到Map

核心代码:

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        // 1. Map保存<排序后的str, List<String>>
        Map<String, List<String>> map = new HashMap<>();
        // 2. 遍历strs, 依次添加到Map
        for (String str : strs){
            char[] charStr = str.toCharArray();
            Arrays.sort(charStr);
            String orderStr = new String(charStr);
            if (map.containsKey(orderStr)){
                map.get(orderStr).add(str);
            }else{
                List<String> temp = new ArrayList<>();
                temp.add(str);
                map.put(orderStr,temp);
            }
        }
        return new ArrayList<List<String>>(map.values());
    }
}

1.3 最长连续序列

题目描述:

  • 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

  • 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

力扣链接:

https://leetcode.cn/problems/longest-consecutive-sequence/description【中等】

解决思路:

  1. 将nums数组的所有元素放入HashSet,去除重复元素
  2. 从序列的最小值开始找,更新最大值

核心代码:

class Solution {
    public int longestConsecutive(int[] nums) {
      	// 1. 将nums数组的所有元素放入HashSet, 去除重复元素
        HashSet<Integer> hs = new HashSet<>();
        for (int i : nums){
            hs.add(i);
        }
        int ans = 0;
        for (int i : hs){
            // 2. 只从序列的最小值开始找
            if (!hs.contains(i - 1)){
                int curAns = 1;
                while(hs.contains(i+1)){
                    i++;
                    curAns++;
                }
                ans = Math.max(ans,curAns);
            }
        }
        return ans; 
    }
}

2. 双指针

2.1 移动零

题目描述:

  • 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

  • 请注意 ,必须在不复制数组的情况下原地对数组进行操作。

力扣链接:

https://leetcode.cn/problems/move-zeroes/【简单】

解题思路:

  • 核心思想:当 left指针指向第一个0,right指针指向第一个非0时,交换元素

  • 解题流程:

    1. 初始化left指针位0,right指针为0

    2. 遍历right指针

      • 如果right指针的元素为0,right++

      • 如果right指针的元素不为0,交换元素,left++,right++

核心代码:

class Solution {
    public void moveZeroes(int[] nums) {
        // 1. 初始化left和right指针为0
        int left = 0;
        int right = 0;
        // 2. 遍历right指针。right指针的元素为0 ? right++ : {swap(l,r), l++, r++}
        while(right < nums.length){
            if (nums[right] == 0){
                right++;
            }else{
                swap(nums,left,right);
                left++;
                right++;
            }
        }
    }

    public void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp; 
    }
}

2.2 盛最多水的容器

题目描述:

  • 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

  • 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

  • 返回容器可以储存的最大水量。

  • **说明:**你不能倾斜容器。

力扣链接:

https://leetcode.cn/problems/container-with-most-water/description/【中等】

解题思路:

  • 初始化左,右指针,最大面积。
  • 计算当前面积, 更新最大面积,短指针向中间移动

核心代码:

class Solution {
    public int maxArea(int[] height) {
        // 1. 初始化左右指针, 最大面积
        int left = 0;
        int right = height.length - 1;
        int maxArea = 0;
        // 2. 计算当前面积, 更新最大面积,短指针向中间移动
        while(left < right){
            int curArea = (right - left) * Math.min(height[left],height[right]);
            maxArea = Math.max(maxArea,curArea);
            if (height[left] < height[right]){
                left++;
            }else{
                right--;
            }
        }
        return maxArea; 
    }
}

2.3 三数之和

题目描述:

  • 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

  • **注意:**答案中不可以包含重复的三元组。

力扣链接:

https://leetcode.cn/problems/3sum/description【中等】

解题思路:

  • 三数之和变成两数之和
  • 避免重复解
  • 直接看代码+注释,理解背下来吧

核心代码:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>(); // 创建一个列表用于存储最终的答案
        
        Arrays.sort(nums); // 首先对数组进行排序,这样可以方便地使用双指针方法查找组合

        for (int i = 0; i < nums.length - 2; ++i){ // 循环遍历数组中的每一个元素,但最后两个元素不需要作为起始点考虑
            if (i > 0 && nums[i] == nums[i-1]) continue; // 如果当前元素和前一个元素相同,则跳过以避免重复解
            
            // 设置目标值为 -nums[i],因为我们需要找到三个数之和为0的组合,即 nums[i] + nums[l] + nums[r] = 0
            int target = - nums[i];
            int l = i + 1; // 左指针初始化为当前元素的下一个位置
            int r = nums.length - 1; // 右指针初始化为数组最后一个元素的位置
            
            while(l < r){ // 当左指针小于右指针时继续循环
                int sum = nums[l] + nums[r]; // 计算左右指针所指元素的和
                
                if (target == sum){ // 如果找到的和正好等于目标值
                    ans.add(Arrays.asList(nums[i], nums[l], nums[r])); // 将找到的三元组添加到答案中
                    
                    // 跳过所有相同的左指针指向的值,避免重复解
                    while(l < r && nums[l] == nums[l + 1]) l++;
                    // 跳过所有相同的右指针指向的值,避免重复解
                    while(l < r && nums[r] == nums[r - 1]) r--;
                    
                    l++; // 移动左指针
                    r--; // 移动右指针
                } else if(target > sum){ // 如果需要更大的和,移动左指针
                    l++;
                }else{ // 如果需要更小的和,移动右指针
                    r--;
                }
            }
        }
        return ans; // 返回所有找到的不重复的三元组
    }
}

2.4 接雨水

题目描述:

  • 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

力扣链接:

https://leetcode.cn/problems/trapping-rain-water/description【困难】

解题思路

  • 对于下标 i,下雨后水能到达的最大高度等于下标 i 两边的最大高度的最小值,下标 i 处能接的雨水量等于下标 i 处的水能到达的最大高度减去 height[i]。

  • leftMax[i] 表示下标 i 及其左边的位置中,height 的最大高度rightMax[i] 表示下标 i 及其右边的位置中,height 的最大高度。

  • 动态规划

  • 计算每个下标i可以接的雨水量:sum[i] = min(i左侧最大高度,i右侧最大高度) - height[i]

image-20250407172249447
注意:本题采用的是动态规划解法。。。理解不了就背这个图。

核心代码

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        if (n == 0) return 0;

        int[] leftMax = new int[n];
        int[] rightMax = new int[n];

        // 填充 leftMax
        int curLeftMax = 0;
        for (int i = 0; i < n; ++i) {
            leftMax[i] = Math.max(height[i], curLeftMax);
            curLeftMax = leftMax[i];  // 更新当前最大值
        }

        // 填充 rightMax
        int curRightMax = 0;
        for (int i = n - 1; i >= 0; --i) {
            rightMax[i] = Math.max(height[i], curRightMax);
            curRightMax = rightMax[i];  // 更新当前最大值
        }

        // 计算能接多少水
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans += Math.min(leftMax[i], rightMax[i]) - height[i];
        }

        return ans;
    }
}

3. 滑动窗口

3.1 无重复字符的最长子串

题目描述:

  • 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。

力扣链接:

https://leetcode.cn/problems/longest-substring-without-repeating-characters/【中等】

解题思路:

  • 双指针滑动
  • Set保存不含重复字符子串

核心代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 使用 HashSet 来记录当前窗口中的字符(便于快速判断是否有重复字符)
        Set<Character> set = new HashSet<>();
        
        // ans:记录最长无重复子串的长度
        // left:滑动窗口左指针
        // right:滑动窗口右指针
        int ans = 0, left = 0, right = 0;

        // 右指针从左向右移动,扩展窗口
        for (; right < s.length(); right++) {
            char ch = s.charAt(right);  // 当前要加入窗口的字符

            // 如果当前字符已经在集合中存在,说明窗口中有重复字符
            // 需要不断将左指针右移,直到重复字符被移除
            while (set.contains(ch)) {
                set.remove(s.charAt(left));  // 移除最左边的字符
                left++;                      // 左指针右移
            }

            // 此时窗口中已经没有重复字符,把当前字符加入集合
            set.add(ch);

            // 计算当前窗口长度,并更新最大值
            ans = Math.max(ans, right - left + 1);
        }

        return ans;  // 返回最长无重复子串的长度
    }
}

3.2 找到字符串中所有字符异位词

题目描述:

  • 给定两个字符串 sp,找到 s 中所有 p异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

踩坑点:题目描述不清晰,两个单词包含相同的字符每个字符出现的次数也相同。

力扣链接:

https://leetcode.cn/problems/find-all-anagrams-in-a-string/description/【中等】

解题思路:

  • 创建两个长度为26的数组分别表示目标字符串p和滑动窗口的字符频率
  • Arrays.equal比较两个数组是否相同(索引对应的值全部一样)
  • 本质:空间换时间

核心代码:

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> ans = new ArrayList<>();
        int len_s = s.length();
        int len_p = p.length();

        // 特殊情况处理
        if (len_s < len_p) return ans;

        // 创建两个长度为26的数组分别表示目标字符串p和滑动窗口的字符频率
        int[] pCount = new int[26];
        int[] windowCount = new int[26];

        // 初始化目标p的字符频率
        for (char ch : p.toCharArray()) {
            pCount[ch - 'a']++;
        }

        // 滑动窗口初始化:前 len_p 个字符
        for (int i = 0; i < len_p; i++) {
            char ch = s.charAt(i);
            windowCount[ch - 'a']++;
        }

        // 如果初始窗口匹配,添加起始索引0
        if (Arrays.equals(pCount, windowCount)) {
            ans.add(0);
        }

        // 开始滑动窗口
        for (int i = len_p; i < len_s; i++) {
            // 移除最左边的字符
            char leftChar = s.charAt(i - len_p);
            windowCount[leftChar - 'a']--;

            // 添加右边新进来的字符
            char rightChar = s.charAt(i);
            windowCount[rightChar - 'a']++;

            // 检查当前窗口是否匹配
            int startIdx = i - len_p + 1;
            if (Arrays.equals(pCount, windowCount)) {
                ans.add(startIdx);
            }
        }

        return ans;
    }
}

未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值