力扣Hot100(Java版本)

写在前文:由于LeetCode100的答案多种多样,本文针对每一题只写一种解法(尽可能简单易懂)。针对不容易理解的题目,采用图片形式 或博主自认为讲解不错的视频链接来提供解题思路。

1. 哈希

1.1 两数之和

题目描述:

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

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

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

力扣链接:

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

解题思路:

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

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

核心代码:

java 复制代码
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

核心代码:

java 复制代码
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. 从序列的最小值开始找,更新最大值

核心代码:

java 复制代码
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++

核心代码:

java 复制代码
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/【中等】

解题思路:

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

核心代码:

java 复制代码
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【中等】

解题思路:

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

核心代码:

java 复制代码
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]

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

核心代码

java 复制代码
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保存不含重复字符子串

核心代码:

java 复制代码
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比较两个数组是否相同(索引对应的值全部一样)
  • 本质:空间换时间

核心代码:

java 复制代码
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;
    }
}

4. 子串

4.1 和为K的子数组

题目描述:

给定一个整数数组 nums 和一个整数 k,找到数组中所有 和为 k 的连续子数组,返回这些子数组的起始索引列表。结果顺序不限。

示例:

输入:nums = [1,1,1], k = 2

输出:[0,1]

解释:子数组 [1,1](起始索引0)和 [1,1](起始索引1)的和均为2。

力扣链接:

https://leetcode.cn/problems/subarray-sum-equals-k/description/【中等】

踩坑点:

  • 前缀和需记录所有出现的位置,而非仅计数,以支持多起点匹配。
  • 哈希表的键为前缀和,值为对应前缀和的所有索引列表。
  • 初始状态需包含前缀和为0时的索引-1,确保边界情况覆盖。

解题思路:

  1. 前缀和与哈希表结合:利用前缀和快速计算子数组和,并通过哈希表记录历史前缀和的位置。
  2. 实时更新与查询 :遍历数组时,计算当前前缀和,检查是否存在 当前前缀和 - k 的历史前缀和,若存在则所有对应位置的下一位即为有效子数组的起点。
  3. 空间换时间:哈希表存储前缀和及其索引列表,牺牲额外空间换取线性时间复杂度。

核心代码:

java 复制代码
import java.util.*;

class Solution {
    public List<Integer> findSubarraySum(int[] nums, int k) {
        List<Integer> result = new ArrayList<>();
        Map<Integer, List<Integer>> prefixSumMap = new HashMap<>();
        prefixSumMap.put(0, new ArrayList<>(Collections.singletonList(-1))); // 初始前缀和0,索引-1
        int sum = 0;
        
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            // 查找是否存在sum - k的前缀和
            if (prefixSumMap.containsKey(sum - k)) {
                for (int startIdx : prefixSumMap.get(sum - k)) {
                    result.add(startIdx + 1);
                }
            }
            // 更新当前前缀和的索引列表
            prefixSumMap.computeIfAbsent(sum, key -> new ArrayList<>()).add(i);
        }
        
        return result;
    }
}

4.2 滑动窗口最大值

题目描述:

  • 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

  • 返回 滑动窗口中的最大值

力扣链接:

https://leetcode.cn/problems/sliding-window-maximum/description/【困难】

解题思路:

题解:https://leetcode.cn/problems/sliding-window-maximum/solutions/2361228/239-hua-dong-chuang-kou-zui-da-zhi-dan-d-u6h0/?envType=study-plan-v2&envId=top-100-liked

  • 维护一个单调递减队列

核心代码:

java 复制代码
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        // 准备工作
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];

        // 未形成窗口
        for (int i = 0; i < k; i++){
            while(!deque.isEmpty() && deque.peekLast() < nums[i])
                deque.removeLast();
            deque.addLast(nums[i]);
        }
        res[0] = deque.peekFirst();

        // 形成窗口
        for (int i = k; i < nums.length; i++){
            if (deque.peekFirst() == nums[i - k])
                deque.removeFirst();
            while(!deque.isEmpty() && deque.peekLast() < nums[i])
                deque.removeLast();
            deque.addLast(nums[i]);
            res[i - k + 1] = deque.peekFirst();
        }
        return res;
    }
}

4.3 最小覆盖子串

题目描述:

  • 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

力扣链接:

https://leetcode.cn/problems/minimum-window-substring/description/【困难】

解题思路:

双指针+两个哈希表。解题思路视频链接: https://www.bilibili.com/video/BV1sJ4m1g727/

核心代码:

java 复制代码
class Solution {
    public String minWindow(String s, String t) {
        int m = s.length(), n = t.length();
        if (m < n) return "";

        // 1. 定义两个HashMap记录s和t中字符出现的次数
        Map<Character, Integer> sMap = new HashMap<>();
        Map<Character, Integer> tMap = new HashMap<>();

        // 2. 初始化sMap和tMap
        for (char ch : t.toCharArray()) {
            tMap.put(ch, tMap.getOrDefault(ch, 0) + 1);
            sMap.put(ch, 0); // 初始化sMap中的键,值设为0
        }

        String curMinStr = "";
        int curMinLen = Integer.MAX_VALUE;

        // 3. 双指针遍历(核心算法)
        int left = 0, right = 0;
        int count = 0; // 记录当前窗口中满足tMap要求的字符个数

        while (right < m) {
            char rightChar = s.charAt(right);
            if (tMap.containsKey(rightChar)) {
                sMap.put(rightChar, sMap.getOrDefault(rightChar, 0) + 1);
                if (sMap.get(rightChar).equals(tMap.get(rightChar))) {
                    count++;
                }
            }
            right++;

            // 当找到一个包含所有t中字符的窗口时,尝试缩小窗口
            while (count == tMap.size()) {
                if (right - left < curMinLen) {
                    curMinLen = right - left;
                    curMinStr = s.substring(left, right);
                }

                char leftChar = s.charAt(left);
                if (sMap.containsKey(leftChar)) {
                    sMap.put(leftChar, sMap.get(leftChar) - 1);
                    if (sMap.get(leftChar) < tMap.get(leftChar)) {
                        count--;
                    }
                }
                left++;
            }
        }

        return curMinStr;
    }

    private boolean matches(Map<Character, Integer> sMap, Map<Character, Integer> tMap) {
        // 遍历tMap的所有key
        for (Character ch : tMap.keySet()){
            if (sMap.getOrDefault(ch, 0) < tMap.get(ch))
                return false;
        }
        return true;
    }
}

5. 普通数组

5.1 最大子数组和

题目描述:

  • 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

  • 子数组是数组中的一个连续部分。

解题思路:

  • 直接看代码

核心代码:

java 复制代码
class Solution {
    public int maxSubArray(int[] nums) {
        int maxSum = nums[0]; // 最大子数组和
        int currentSum = nums[0];  // 当前子数组和

        for(int i = 1; i < nums.length; ++i){
            // 更新当前子数组和(如果当前子数组和变大则更新,若不变大则跳过)
            currentSum = Math.max(nums[i] + currentSum, nums[i]);
            // 更新最大子数组和
            maxSum = Math.max(maxSum, currentSum);
        }
        return maxSum;
    }
}

5.2 合并区间

题目描述:

  • 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

解题思路:

力扣链接:

https://leetcode.cn/problems/merge-intervals/description/【中等】

核心代码:

java 复制代码
class Solution {
    public int[][] merge(int[][] intervals) {
        // 1. 将列表中的区间按照左端点升序排序
        Arrays.sort(intervals,new Comparator<int[]>(){
            public int compare(int[] interval1, int[] interval2){
                return interval1[0] - interval2[0];
            }
        });

        List<int[]> merged = new ArrayList<>();
        // 2. 遍历intervals
        for (int i = 0; i < intervals.length; ++i){
            int L = intervals[i][0], R = intervals[i][1];
            // 增加值
            if (merged.size() == 0 || merged.get(merged.size()-1)[1] < L){
                merged.add(new int[]{L,R});
            }else{
                // 更新端点
                merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() -1)[1],R);
            }         
        }
        return merged.toArray(new int[merged.size()][]);
    }
}

5.3 轮转数组

题目描述:

  • 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

解题思路:

  • 对k取模
  • 反转三次

力扣链接:

https://leetcode.cn/problems/rotate-array/description/【中等】

核心代码:

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        // !!!踩坑点, 对k取模
        int n = nums.length;
        k = k % n; // 避免 k 大于数组长度的情况
        if (k == 0) return;

        reverse(nums,0,nums.length-1);
        reverse(nums,0,k-1);
        reverse(nums,k,nums.length-1);
    }
    public void reverse(int[] nums,int l, int r){
        for (int i = l,j = r; i < j; i++,j--){
            int temp = nums[j];
            nums[j] = nums[i];
            nums[i] = temp;
        }
    }
}

5.4 除自身以外的数组

题目描述:

  • 给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

  • 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

  • 请 **不要使用除法,**且在 O(n) 时间复杂度内完成此题。

力扣链接:

https://leetcode.cn/problems/product-of-array-except-self/description/【中等】

解题思路:

使用了两个辅助数组 leftCurrightCur 来分别记录每个元素左边和右边所有数的乘积:

  • leftCur[i] 表示 nums[0] * nums[1] * ... * nums[i-1]
  • rightCur[i] 表示 nums[i+1] * nums[i+2] * ... * nums[n-1]
  • 最终结果是:ans[i] = leftCur[i] * rightCur[i]

这个方法的时间复杂度是 O(n) ,空间复杂度也是 O(n)(因为用了两个额外的数组)。

核心代码:

java 复制代码
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] leftCur = new int[n];
        int[] rightCur = new int[n];

        leftCur[0] = 1;
        for (int i = 1; i < n; ++i){
            leftCur[i] = leftCur[i - 1] * nums[i - 1]; // 
        }

        rightCur[n - 1] = 1;
        for (int j = n - 2; j >= 0; --j) {
            rightCur[j] = rightCur[j + 1] * nums[j + 1]; //
        }

        int[] ans = new int[n];
        for (int i = 0; i < n; ++i) {
            ans[i] = leftCur[i] * rightCur[i];
        }

        return ans;
    }
}

5.4 缺失的第一个正数

题目描述:

  • 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

  • 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

力扣链接:

https://leetcode.cn/problems/first-missing-positive/【困难】

解题思路:

  • 结果只可能属于[1,N+1],其中N是数组的长度
  • 遍历nums数组,将其中元素值属于[1,N]的元素插入nums数组

核心代码:

java 复制代码
class Solution {
    public int firstMissingPositive(int[] nums) {
        // 1. 用于保存所有[1,N]之间的正数(初始化为0)
        int[] arr = new int[nums.length];
        // 2. 填充数组
        for (int i = 0; i < nums.length; i++){
            if (nums[i] > 0 && nums[i] <= nums.length){
                arr[nums[i]-1] = nums[i];
            }
        }
        // 3. 找到第一个为0的数组
        for (int i = 0; i < nums.length; i++){
            if (arr[i] == 0){
                return i+1;
            }
        }
        return nums.length+1;
    }
}

6. 矩阵

6.1 矩阵置零

题目描述:

  • 给定一个 *m* x *n* 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。**

力扣链接:

https://leetcode.cn/problems/set-matrix-zeroes/description/【中等】

解题思路:

  • new两个数组,一个保存行,一个保存列
  • 看代码吧

核心代码:

java 复制代码
class Solution {
    public void setZeroes(int[][] matrix) {
        int m = matrix.length; // 总行数
        int n = matrix[0].length; // 总列数

        // boolean数组默认初始值是false
        boolean[] row = new boolean[m]; // row[i]为true表示matrix的第i行需要填0
        boolean[] col = new boolean[n]; // col[i]为true表示matrix的第i列需要填0

        for (int i = 0; i < m; ++i){
            for (int j = 0; j < n; ++j){
                if (matrix[i][j] == 0){
                    row[i] = true;
                    col[j] = true;
                }
            }
        }
        // 将matrix的行填充0
        for (int i = 0; i < m; ++i){
            if (row[i] == true){
                for (int j = 0; j < n; j++){
                    matrix[i][j] = 0;
                }
            }
        }
        // 将matrix的列填充0
        for (int i = 0; i < n; ++i){
            if (col[i] == true){
                for (int j = 0; j < m; j++){
                    matrix[j][i] = 0;
                }
            }
        }
        
    }
}

6.2 螺旋矩阵

题目描述:

  • 给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

力扣链接:

https://leetcode.cn/problems/spiral-matrix/solutions/2362055/54-luo-xuan-ju-zhen-mo-ni-qing-xi-tu-jie-juvi/【中等】

解题思路:

https://leetcode.cn/problems/spiral-matrix/solutions/2362055/54-luo-xuan-ju-zhen-mo-ni-qing-xi-tu-jie-juvi/

核心代码:

java 复制代码
class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        if(matrix.length == 0)
            return new ArrayList<Integer>();
        // l表示左边界索引,r表示右边届索引,t表示最上索引,b表示最下索引
        int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1;
        int x = 0;
        Integer[] res = new Integer[(r+1)*(b+1)];

        while(true){
            for (int i = l; i <= r; i++) res[x++] = matrix[t][i]; // left to right
            if (++t > b) break;
            for (int i = t; i <=b; i++) res[x++] = matrix[i][r]; // top to bottom
            if (l > --r) break;
            for (int i = r; i >= l; i--) res[x++] = matrix[b][i]; // right to left
            if (t > --b) break;
            for (int i = b; i >=t; i--) res[x++] = matrix[i][l]; // bottom to top
            if (++l > r) break;
        }
        return Arrays.asList(res);
    }
}

6.3 旋转图像

题目描述:

  • 给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

  • 你必须在**原地** 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

力扣链接:

https://leetcode.cn/problems/rotate-image/description/【中等】

解题思路:

  • 先转置
  • 再沿对称轴交换

核心代码:

java 复制代码
class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        // Step1: 转置矩阵
        for (int i = 0; i < n; ++i){
            for (int j = i+1; j < n; ++j){
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
        // Step 2: 每一行反转(左右翻转)
        for (int i = 0; i < n; i++) { 
            for (int j = 0; j < n / 2; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[i][n - 1 - j];
                matrix[i][n - 1 - j] = temp;
            }
        }
    }
}

6.4 搜索二维矩阵

题目描述:

编写一个高效的算法来搜索 *m* x *n* 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

力扣链接:

https://leetcode.cn/problems/search-a-2d-matrix-ii/description【中等】

解题思路:

  • 将矩阵逆时针旋转45度,变成一个图
  • 二叉搜索树

核心代码:

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;            // 行数
        int n = matrix[0].length;         // 列数

        int row = 0;
        int col = n - 1;                  // 从右上角开始

        while (row < m && col >= 0) {
            int cur = matrix[row][col];
            if (cur == target) {
                return true;
            } else if (cur > target) {
                col--; // 向左移一列
            } else {
                row++; // 向下移一行
            }
        }

        return false;
    }
}

7. 链表

7.1 相交链表

题目描述:

  • 给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

    图示两个链表在节点 c1 开始相交

力扣链接:

https://leetcode.cn/problems/intersection-of-two-linked-lists/description/【简单】

解题思路:

  • a-c+x+b=b-c+x+a
  • 两个指针 AB 分别从链表 A 和 B 的头节点开始遍历:
    • 如果当前节点不为 null,就向后移动;
    • 如果到了末尾(即当前节点为 null),就跳到另一个链表的头部继续遍历;
    • 当两个指针相等时,要么是在相交点 ,要么就是在都为 null 的时候(也就是不相交的情况)。

核心代码:

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode A = headA, B = headB;
        while(A != B){
            A = A != null ? A.next : headB;
            B = B !=null ? B.next : headA;
        }
        return A;   
    }
}

7.2 反转链表

题目描述:

  • 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

​ 踩坑点:初始化pre和cur结点要正确。

  • 核心代码:

    java 复制代码
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode() {}
     *     ListNode(int val) { this.val = val; }
     *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
     * }
     */
    class Solution {
        public ListNode reverseList(ListNode head) {
            ListNode cur = head, pre = null;
            while (cur != null){
                ListNode temp = cur.next; // 暂存后继节点
                cur.next = pre;
                pre = cur;
                cur = temp;
            }
            return pre;
        }
    }

7.3 回文链表

题目描述:

  • 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

力扣链接:

https://leetcode.cn/problems/palindrome-linked-list/description/【简单】

解题思路:

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
      
        List<Integer> list = new ArrayList<>();
        ListNode cur = head;
        while(cur != null){
            list.add(cur.val);
            cur = cur.next;
        }
      
        Integer[] arr = list.toArray(new Integer[0]); // 其中new Integer[0]的作用是告诉list集合的返回类型是Integer类型

        int l = 0, r = arr.length - 1;
        while(l < r){
            if (arr[l] == arr[r]){
                l++;
                r--;
            }else{
                return false;
            }
        }
        return true;
   
    }
}

7.4 环形链表

题目描述:

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

力扣链接:

https://leetcode.cn/problems/linked-list-cycle/description/【简单】

解题思路:

  • 快慢指针:若一个链表有环,则快慢指针终会相遇
  • 快慢指针初始化:慢指针索引0开始,快指针索引1开始

核心代码:

java 复制代码
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        // 排除边界值(0个/1一个结点的情况)
        if (head == null || head.next == null){
            return false;
        }
        // 快慢指针(>=2个结点的情况)
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }
}

7.5 环形链表||

题目描述:

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始 )。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

力扣链接:

https://leetcode.cn/problems/linked-list-cycle-ii/description/【中等】

解题思路:

https://leetcode.cn/problems/linked-list-cycle-ii/solutions/12616/linked-list-cycle-ii-kuai-man-zhi-zhen-shuang-zhi-

  • 快慢指针两次相遇
    • 第一相遇之后,快指针从头开始一步一步走

核心代码:

java 复制代码
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        // 初始化快慢指针位于head
        ListNode fast = head, slow = head;

        // 快慢指针第一次相遇
        while(true){
            if (fast == null || fast.next == null) return null;
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) break;
        }
        // 快慢指针第二次相遇
        fast = head;
        while(slow != fast){
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }
}

7.5 合并两个有序链表

题目描述:

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

力扣链接:

https://leetcode.cn/problems/merge-two-sorted-lists/description/【简单】

解题思路:

双指针

核心代码:

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode left = new ListNode();
        ListNode cur = left;
        while(list1 != null && list2 != null){
            if (list1.val < list2.val){
                cur.next = list1;
                list1 = list1.next;
            }else{
                cur.next = list2;
                list2 = list2.next;
            }
            cur = cur.next;
        }
        cur.next = list1 != null ? list1 : list2;
        return left.next;
    }
}

7.6 两数相加

题目描述:

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

力扣链接:

https://leetcode.cn/problems/add-two-numbers/description/【中等】

解题思路:

双指针

核心代码:

java 复制代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {

        ListNode head = new ListNode();
        ListNode cur = head;
        int temp = 0;
        while(l1 !=null || l2 !=null){
            int l1Val;
            int l2Val;
            if (l1 == null) {
                l1Val = 0;
            }else{
                l1Val = l1.val;
                l1 = l1.next;
            }
            if (l2 == null) {
                l2Val = 0;
            }else{
                l2Val = l2.val;
                l2 = l2.next;
            }
            
            int total = l1Val + l2Val + temp;
            temp = total / 10;
            int val = total % 10;

            ListNode newNode = new ListNode(val);
            cur.next = newNode;
            cur = cur.next;
        }
        
        if (temp != 0){
            ListNode newNode = new ListNode(temp);
            cur.next = newNode;
            cur = cur.next;
        }
        return head.next;
        
    }
}

未完待续。。。

相关推荐
自信150413057592 小时前
数据结构之单链表OJ复盘
c语言·数据结构·算法
仰泳的熊猫2 小时前
题目2265:蓝桥杯2015年第六届真题-移动距离
开发语言·数据结构·c++·算法·蓝桥杯
开开心心就好2 小时前
Word批量转PDF工具,仅转换不合并很实用
java·前端·人工智能·edge·pdf·语音识别·模块测试
共享家95272 小时前
Java入门
java·开发语言
星辰_mya2 小时前
ThreadLocal 与内存泄漏
java·开发语言
wuxinyan1232 小时前
Java面试题42:一文深入了解AI Coding 工具
java·人工智能·面试题·ai coding
¿i?2 小时前
LinkedList 含iterator写法的理解
java·开发语言
篮l球场2 小时前
合并 K 个升序链表
算法
苦藤新鸡2 小时前
87.分割成两个等和数组 leetcode416
数据结构·算法·leetcode