算法题盘点总结

1. 反转字符串

题目:编写一个函数,反转给定的字符串。
解答思路:
使用 StringBuilder 或 StringBuffer 的 reverse() 方法。
或者手动实现,通过交换字符串两端的字符

public String reverseString(String s) {
   
    return new StringBuilder(s).reverse().toString();
    // 或者手动实现
    char[] chars = s.toCharArray();
    int left = 0, right = chars.length - 1;
    while (left < right) {
   
        char temp = chars[left];
        chars[left] = chars[right];
        chars[right] = temp;
        left++;
        right--;
    }
    return new String(chars);
}

2. 判断回文字符串

题目:编写一个函数,判断给定的字符串是否为回文。
解答思路:
将字符串反转后与原字符串比较。
或者使用双指针方法,从两端向中间比较字符。

public boolean isPalindrome(String s) {
   
    int left = 0, right = s.length() - 1;
    while (left < right) {
   
        if (s.charAt(left) != s.charAt(right)) {
   
            return false;
        }
        left++;
        right--;
    }
    return true;
}

3. 斐波那契数列

题目:编写一个函数,计算斐波那契数列的第 n 项。
解答思路:
使用递归(注意效率问题)。
使用动态规划或迭代方法以提高效率。

public int fibonacci(int n) {
   
    if (n <= 1) {
   
        return n;
    }
    int a = 0, b = 1, c = 0;
    for (int i = 2; i <= n; i++) {
   
        c = a + b;
        a = b;
        b = c;
    }
    return c;
}

4. 数组去重

题目:编写一个函数,去除数组中的重复元素。
解答思路:使用 HashSet 来存储唯一元素。
或者使用双指针方法对已排序数组进行去重。

public int[] removeDuplicates(int[] nums) {
   
    if (nums.length == 0) {
   
        return new int[0];
    }
    Set<Integer> set = new HashSet<>();
    for (int num : nums) {
   
        set.add(num);
    }
    int[] result = new int[set.size()];
    int index = 0;
    for (int num : set) {
   
        result[index++] = num;
    }
    // 如果需要保持顺序,可以使用 LinkedHashSet
    return result;
}
// 或者使用排序后的双指针方法
public int[] removeDuplicatesSorted(int[] nums) {
   
    if (nums.length == 0) {
   
        return new int[0];
    }
    int i = 0;
    for (int j = 1; j < nums.length; j++) {
   
        if (nums[j] != nums[i]) {
   
            i++;
            nums[i] = nums[j];
        }
    }
    return Arrays.copyOfRange(nums, 0, i + 1);
}

5. 二分查找

题目:编写一个函数,在已排序的数组中查找给定的元素。
解答思路:
使用二分查找算法。

public int binarySearch(int[] nums, int target) {
   
    int left = 0, right = nums.length - 1;
    while (left <= right) {
   
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
   
            return mid;
        } else if (nums[mid] < target) {
   
            left = mid + 1;
        } else {
   
            right = mid - 1;
        }
    }
    return -1;
}

6.俩数之和

public int[] twoSum(int[] nums, int target) {
   
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
   
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
   
            return new int[] {
    map.get(complement), i };
        }
        map.put(nums[i], i);
    }
    throw new IllegalArgumentException("No two sum solution");
}

7.反转列表

public ListNode reverseList(ListNode head) {
   
    ListNode prev = null;
    ListNode curr = head;
    while (curr != null) {
   
        ListNode nextTemp = curr.next;
        curr.next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}

8.LRU缓存实现

class LRUCache {
   
    class DLinkedNode {
   
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
    }
    
    private void addNode(DLinkedNode node) {
   
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }
    
    private void removeNode(DLinkedNode node) {
   
        DLinkedNode prev = node.prev;
        DLinkedNode next = node.next;
        prev.next = next;
        next.prev = prev;
    }
    
    private void moveToHead(DLinkedNode node) {
   
        removeNode(node);
        addNode(node);
    }
    
    private DLinkedNode popTail() {
   
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }
    
    private Map<Integer, DLinkedNode> cache = new HashMap<>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;
    
    public LRUCache(int capacity) {
   
        this.size = 0;
        this.capacity = capacity;
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }
    
    public int get(int key) {
   
        DLinkedNode node = cache.get(key);
        if (node == null) return -1;
        moveToHead(node);
        return node.value;
    }
    
    public void put(int key, int value) {
   
        DLinkedNode node = cache.get(key);
        if (node == null) {
   
            DLinkedNode newNode = new DLinkedNode();
            newNode.key = key;
            newNode.value = value;
            cache.put(key, newNode);
            addNode(newNode);
            ++size;
            if (size > capacity) {
   
                DLinkedNode tail = popTail();
                cache.remove(tail.key);
                --size;
            }
        } else {
   
            node.value = value;
            moveToHead(node);
        }
    }
}

9. 字母异位词分组

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

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
在这里插入图片描述

import java.util.*;

public class AnagramGrouper {
   
    public List<List<String>> groupAnagrams(String[] strs) {
   
        // 使用 HashMap 来存储分组结果
        Map<String, List<String>> map = new HashMap<>();
        
        for (String str : strs) {
   
            // 将字符串转换为字符数组并排序
            char[] charArray = str.toCharArray();
            Arrays.sort(charArray);
            // 将排序后的字符数组转换回字符串,作为哈希表的键
            String sortedStr = new String(charArray);
            
            // 如果哈希表中不存在该键,则创建一个新的列表
            if (!map.containsKey(sortedStr)) {
   
                map.put(sortedStr, new ArrayList<>());
            }
            // 将原始字符串添加到对应键的列表中
            map.get(sortedStr).add(str);
        }
        
        // 返回哈希表中的所有值,即字母异位词的分组
        return new ArrayList<>(map.values());
    }

    public static void main(String[] args) {
   
        AnagramGrouper grouper = new AnagramGrouper();
        String[] strs = {
   "eat", "tea", "tan", "ate", "nat", "bat"};
        List<List<String>> groupedAnagrams = grouper.groupAnagrams(strs);
        
        // 打印结果
        for (List<String> group : groupedAnagrams) {
   
            System.out.println(group);
        }
    }
}

在这里插入图片描述

10.最长连续序列

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

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

在这里插入图片描述

import java.util.HashSet;
import java.util.Set;

public class LongestConsecutiveSequence {
   
    public int longestConsecutive(int[] nums) {
   
        if (nums == null || nums.length == 0) {
   
            return 0;
        }

        Set<Integer> numSet = new HashSet<>();
        for (int num : nums) {
   
            numSet.add(num);
        }

        int longestStreak = 0;

        for (int num : numSet) {
   
            // 只有当 num 是序列的起点时才开始计算
            if (!numSet.contains(num - 1)) {
   
                int currentNum = num;
                int currentStreak = 1;

                while (numSet.contains(currentNum + 1)) {
   
                    currentNum += 1;
                    currentStreak += 1;
                }

                longestStreak = Math.max(longestStreak, currentStreak);
            }
        }

        return longestStreak;
    }

    public static void main(String[] args) {
   
        LongestConsecutiveSequence solution = new LongestConsecutiveSequence();
        int[] nums = {
   100, 4, 200, 1, 3, 2};
        System.out.println("The length of the longest consecutive sequence is: " + solution.longestConsecutive(nums));
    }
}

在这里插入图片描述

11. 移动零

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

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

在这里插入图片描述

public class MoveZeroes {
   
    public void moveZeroes(int[] nums) {
   
        if (nums == null || nums.length == 0) {
   
            return;
        }

        int nonZeroIndex = 0; // 用于记录下一个非零元素应该放置的位置

        // 遍历数组,将所有非零元素移动到前面
        for (int i = 0; i < nums.length; i++) {
   
            if (nums[i] != 0) {
   
                nums[nonZeroIndex] = nums[i];
                nonZeroIndex++;
            }
        }

        // 将剩余的位置填充为零
        for (int i = nonZeroIndex; i < nums.length; i++) {
   
            nums[i] = 0;
        }
    }

    public static void main(String[] args) {
   
        MoveZeroes solution = new MoveZeroes();
        int[] nums = {
   0, 1, 0, 3, 12};
        solution.moveZeroes(nums);

        // 打印结果
        for (int num : nums) {
   
            System.out.print(num + " ");
        }
    }
}

12.盛最多水的容器

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

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

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

说明:你不能倾斜容器。
示例 1:

在这里插入图片描述

输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:

输入:height = [1,1]
输出:1

在这里插入图片描述

public class ContainerWithMostWater {
   
    public int maxArea(int[] height) {
   
        if (height == null || height.length < 2) {
   
            return 0;
        }

        int left = 0;
        int right = height.length - 1;
        int maxArea = 0;

        while (left < right) {
   
            // 计算当前容器的面积
            int currentArea = Math.min(height[left], height[right]) * (right - left);
            // 更新最大面积
            maxArea = Math.max(maxArea, currentArea);

            // 移动较短的线,尝试找到更大的面积
            if (height[left] < height[right]) {
   
                left++;
            } else {
   
                right--;
            }
        }

        return maxArea;
    }

    public static void main(String[] args) {
   
        ContainerWithMostWater solution = new ContainerWithMostWater();
        int[] height = {
   1, 8, 6, 2, 5, 4, 8, 3, 7};
        System.out.println("Maximum area: " + solution.maxArea(height));
    }
}

在这里插入图片描述

13.三数之和

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

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

3 <= nums.length <= 3000
-105 <= nums[i] <= 105

在这里插入图片描述

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ThreeSum {
   
    public List<List<Integer>> threeSum(int[] nums) {
   
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length < 3) {
   
            return result;
        }

        // 对数组进行排序
        Arrays.sort(nums);

        for (int i = 0; i < nums.length - 2; i++) {
   
            // 跳过重复的元素
            if (i > 0 && nums[i] == nums[i - 1]) {
   
                continue;
            }

            int left = i + 1;
            int right = nums.length - 1;

            while (left < right) {
   
                int sum = nums[i] + nums[left] + nums[right];

                if (sum == 0) {
   
                    // 找到一个三元组
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));

                    // 跳过重复的元素
                    while (left < right && nums[left] == nums[left + 1]) {
   
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
   
                        right--;
                    }

                    // 移动指针
                    left++;
                    right--;
                } else if (sum < 0) {
   
                    // 如果和小于零,移动左指针以增加和
                    left++;
                } else {
   
                    // 如果和大于零,移动右指针以减小和
                    right--;
                }
            }
        }

        return result;
    }

    public static void main(String[] args) {
   
        ThreeSum solution = new ThreeSum();
        int[] nums = {
   -1, 0, 1, 2, -1, -4};
        List<List<Integer>> result = solution.threeSum(nums);

        // 打印结果
        for (List<Integer> triplet : result) {
   
            System.out.println(triplet);
        }
    }
}

在这里插入图片描述

14.接雨水

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

示例 1:

在这里插入图片描述

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105

在这里插入图片描述

public class TrappingRainWater {
   
    public int trap(int[] height) {
   
        if (height == null || height.length < 3) {
   
            return 0;
        }

        int left = 0;
        int right = height.length - 1;
        int leftMax = 0;
        int rightMax = 0;
        int trappedWater = 0;

        while (left < right) {
   
            if (height[left] < height[right]) {
   
                // 如果左侧柱子较低
                if (height[left] >= leftMax) {
   
                    leftMax = height[left];
                } else {
   
                    trappedWater += leftMax - height[left];
                }
                left++;
            } else {
   
                // 如果右侧柱子较低或相等
                if (height[right] >= rightMax) {
   
                    rightMax = height[right];
                } else {
   
                    trappedWater += rightMax - height[right];
                }
                right--;
            }
        }

        return trappedWater;
    }

    public static void main(String[] args) {
   
        TrappingRainWater solution = new TrappingRainWater();
        int[] height = {
   0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1};
        System.out.println("Trapped water: " + solution.trap(height));
    }
}

在这里插入图片描述
在这里插入图片描述

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

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

示例 1:

输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

提示:

0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

在这里插入图片描述

import java.util.HashSet;
import java.util.Set;

public class LongestSubstringWithoutRepeatingCharacters {
   
    public int lengthOfLongestSubstring(String s) {
   
        if (s == null || s.length() == 0) {
   
            return 0;
        }

        Set<Character> set = new HashSet<>();
        int maxLength = 0;
        int left = 0;

        for (int right = 0; right < s.length(); right++) {
   
            // 如果字符已经存在于集合中,移动左指针直到移除重复字符
            while (set.contains(s.charAt(right))) {
   
                set.remove(s.charAt(left));
                left++;
            }

            // 将当前字符添加到集合中
            set.add(s.charAt(right));

            // 更新最大长度
            maxLength = Math.max(maxLength, right - left + 1);
        }

        return maxLength;
    }

    public static void main(String[] args) {
   
        LongestSubstringWithoutRepeatingCharacters solution = new LongestSubstringWithoutRepeatingCharacters();
        String s = "abcabcbb";
        System.out.println("Length of longest substring without repeating characters: " + solution.lengthOfLongestSubstring(s));
    }
}

在这里插入图片描述
在这里插入图片描述

16.找到字符串中所有字母异位词

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

示例 1:

输入: s = “cbaebabacd”, p = “abc”
输出: [0,6]
解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的异位词。
示例 2:

输入: s = “abab”, p = “ab”
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 “ab”, 它是 “ab” 的异位词。
起始索引等于 1 的子串是 “ba”, 它是 “ab” 的异位词。
起始索引等于 2 的子串是 “ab”, 它是 “ab” 的异位词。

提示:

1 <= s.length, p.length <= 3 * 104
s 和 p 仅包含小写字母

在这里插入图片描述

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class FindAllAnagramsInAString {
   
    public List<Integer> findAnagrams(String s, String p) {
   
        List<Integer> result = new ArrayList<>();
        if (s == null || p == null || s.length() < p.length()) {
   
            return result;
        }

        int[] pCount = new int[26]; // 用于存储模式字符串 p 中每个字符的频率
        int[] sCount = new int[26]; // 用于存储当前窗口中每个字符的频率

        // 初始化模式字符串 p 的字符频率
        for (char c : p.toCharArray()) {
   
            pCount[c - 'a']++;
        }

        int pLength = p.length();
        int sLength = s.length();

        // 滑动窗口遍历字符串 s
        for (int i = 0; i < sLength; i++) {
   
            // 增加当前字符到窗口中
            sCount[s.charAt(i) - 'a']++;

            // 如果窗口大小超过 p 的长度,移除最左侧字符
            if (i >= pLength) {
   
                sCount[s.charAt(i - pLength) - 'a']--;
            }

            // 检查当前窗口是否与模式字符串 p 的字符频率相同
            if (Arrays.equals(pCount, sCount)) {
   
                result.add(i - pLength + 1);
            }
        }

        return result;
    }

    public static void main(String[] args) {
   
        FindAllAnagramsInAString solution = new FindAllAnagramsInAString();
        String s = "cbaebabacd";
        String p = "abc";
        List<Integer> result = solution.findAnagrams(s, p);
        System.out.println("Indices of anagrams: " + result);
    }
}

在这里插入图片描述
在这里插入图片描述

17.和为 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。

子数组是数组中元素的连续非空序列。

示例 1:

输入:nums = [1,1,1], k = 2
输出:2
示例 2:

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

提示:

1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107

在这里插入图片描述

import java.util.HashMap;
import java.util.Map;

public class SubarraySumEqualsK {
   
    public int subarraySum(int[] nums, int k) {
   
        if (nums == null || nums.length == 0) {
   
            return 0;
        }

        int count = 0;
        int sum = 0;
        Map<Integer, Integer> prefixSumCount = new HashMap<>();
        // 初始化前缀和为0的情况,表示从数组开始到当前位置的子数组和为k的次数(即初始时有一个空子数组)
        prefixSumCount.put(0, 1);

        for (int num : nums) {
   
            sum += num;

            // 检查是否存在一个前缀和,使得当前前缀和减去该前缀和等于k
            if (prefixSumCount.containsKey(sum - k)) {
   
                count += prefixSumCount.get(sum - k);
            }

            // 更新当前前缀和的出现次数
            prefixSumCount.put(sum, prefixSumCount.getOrDefault(sum, 0) + 1);
        }

        return count;
    }

    public static void main(String[] args) {
   
        SubarraySumEqualsK solution = new SubarraySumEqualsK();
        int[] nums = {
   1, 1, 1};
        int k = 2;
        System.out.println("Number of subarrays with sum " + k + ": " + solution.subarraySum(nums, k));
    }
}

在这里插入图片描述
在这里插入图片描述

17.滑动窗口最大值

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

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

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:

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

提示:

1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length

在这里插入图片描述

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Arrays;

public class SlidingWindowMaximum {
   
    public int[] maxSlidingWindow(int[] nums, int k) {
   
        if (nums == null || nums.length == 0 || k <= 0) {
   
            return new int[0];
        }

        int n = nums.length;
        int[] result = new int[n - k + 1];
        Deque<Integer> deque = new ArrayDeque<>();

        for (int i = 0; i < n; i++) {
   
            // 移除不在窗口范围内的元素索引
            if (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
   
                deque.pollFirst();
            }

            // 移除所有比当前元素小的元素索引,因为它们不可能成为窗口的最大值
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
   
                deque.pollLast();
            }

            // 添加当前元素的索引
            deque.offerLast(i);

            // 当窗口大小达到 k 时,记录当前窗口的最大值
            if (i >= k - 1) {
   
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }

        return result;
    }

    public static void main(String[] args) {
   
        SlidingWindowMaximum solution = new SlidingWindowMaximum();
        int[] nums = {
   1, 3, -1, -3, 5, 3, 6, 7};
        int k = 3;
        int[] result = solution.maxSlidingWindow(nums, k);
        System.out.println("Max values in sliding window: " + Arrays.toString(result));
    }
}

在这里插入图片描述
在这里插入图片描述

18.最小覆盖子串

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

注意:

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

示例 1:

输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。
示例 2:

输入:s = “a”, t = “a”
输出:“a”
解释:整个字符串 s 是最小覆盖子串。
示例 3:

输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

m == s.length
n == t.length
1 <= m, n <= 105
s 和 t 由英文字母组成

在这里插入图片描述

import java.util.HashMap;
import java.util.Map;

public class MinimumWindowSubstring {
   
    public String minWindow(String s, String t) {
   
        if (s == null || t == null || s.length() == 0 || t.length() == 0) {
   
            return "";
        }

        // 用于存储 t 中每个字符的频率
        Map<Character, Integer> targetCount = new HashMap<>();
        for (char c : t.toCharArray()) {
   
            targetCount.put(c, targetCount.getOrDefault(c, 0) + 1);
        }

        // 滑动窗口中的字符频率
        Map<Character, Integer> windowCount = new HashMap<>();
        int left = 0, right = 0;
        int valid = 0; // 记录窗口中满足 t 字符频率的字符种类数
        int start = 0, minLength = Integer.MAX_VALUE;

        while (right < s.length()) {
   
            char c = s.charAt(right);
            right++;

            // 更新窗口中的字符频率
            if (targetCount.containsKey(c)) {
   
                windowCount.put(c, windowCount.getOrDefault(c, 0) + 1);
                if (windowCount.get(c).equals(targetCount.get(c))) {
   
                    valid++;
                }
            }

            // 判断左侧窗口是否要收缩
            while (valid == targetCount.size()) {
   
                // 更新最小覆盖子串
                if (right - left < minLength) {
   
                    start = left;
                    minLength = right - left;
                }

                char d = s.charAt(left);
                left++;

                // 更新窗口中的字符频率
                if (targetCount.containsKey(d)) {
   
                    if (windowCount.get(d).equals(targetCount.get(d))) {
   
                        valid--;
                    }
                    windowCount.put(d, windowCount.get(d) - 1);
                }
            }
        }

        return minLength == Integer.MAX_VALUE ? "" : s.substring(start, start + minLength);
    }

    public static void main(String[] args) {
   
        MinimumWindowSubstring solution = new MinimumWindowSubstring();
        String s = "ADOBECODEBANC";
        String t = "ABC";
        System.out.println("Minimum window substring: " + solution.minWindow(s, t));
    }
}

在这里插入图片描述
在这里插入图片描述

19. 最大子数组和

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

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

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:

输入:nums = [1]
输出:1
示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

提示:

1 <= nums.length <= 105
-104 <= nums[i] <= 104
在这里插入图片描述

public class MaximumSubarraySum {
   
    public int maxSubArray(int[] nums) {
   
        if (nums == null || nums.length == 0) {
   
            return 0; // 或者抛出异常,根据需求处理
        }

        int currentSum = nums[0];
        int maxSum = 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;
    }

    public static void main(String[] args) {
   
        MaximumSubarraySum solution = new MaximumSubarraySum();
        int[] nums = {
   -2, 1, -3, 4, -1, 2, 1, -5, 4};
        System.out.println("Maximum subarray sum: " + solution.maxSubArray(nums)); // 输出: 6
    }
}

在这里插入图片描述

20.合并区间

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

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示:

1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104

在这里插入图片描述

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MergeIntervals {
   
    public int[][] merge(int[][] intervals) {
   
        if (intervals == null || intervals.length == 0) {
   
            return new int[0][0];
        }

        // Step 1: Sort intervals based on the start time
        Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));

        List<int[]> merged = new ArrayList<>();
        int[] currentInterval = intervals[0];
        merged.add(currentInterval);

        for (int[] interval : intervals) {
   
            int currentEnd = currentInterval[1];
            int nextStart = interval[0];
            int nextEnd = interval[1];

            if (currentEnd >= nextStart) {
   
                // Overlapping intervals, merge them
                currentInterval[1] = Math.max(currentEnd, nextEnd);
            } else {
   
                // Non-overlapping interval, add it to the list
                currentInterval = interval;
                merged.add(currentInterval);
            }
        }

        // Convert List<int[]> to int[][]
        return merged.toArray(new int[merged.size()][]);
    }

    public static void main(String[] args) {
   
        MergeIntervals solution = new MergeIntervals();
        int[][] intervals = {
   {
   1, 3}, {
   2, 6}, {
   8, 10}, {
   15, 18}};
        int[][] mergedIntervals = solution.merge(intervals);

        for (int[] interval : mergedIntervals) {
   
            System.out.println("[" + interval[0] + ", " + interval[1] + "]");
        }
    }
}

在这里插入图片描述

21.轮转数组

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

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

提示:

1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105

在这里插入图片描述

public class RotateArray {
   
    public void rotate(int[] nums, int k) {
   
        if (nums == null || nums.length == 0 || k <= 0) {
   
            return;
        }
        
        int n = nums.length;
        k = k % n; // 处理 k 大于数组长度的情况
        
        // 辅助方法:反转数组的指定部分
        reverse(nums, 0, n - 1);       // 反转整个数组
        reverse(nums, 0, k - 1);       // 反转前 k 个元素
        reverse(nums, k, n - 1);       // 反转剩余的元素
    }
    
    private void reverse(int[] nums, int start, int end) {
   
        while (start < end) {
   
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
    
    public static void main(String[] args) {
   
        RotateArray solution = new RotateArray();
        int[] nums = {
   1, 2, 3, 4, 5, 6, 7};
        int k = 3;
        solution.rotate(nums, k);
        
        // 输出旋转后的数组
        for (int num : nums) {
   
            System.out.print(num + " ");
        }
        // 输出: 5 6 7 1 2 3 4
    }
}

在这里插入图片描述

22.除自身以外数组的乘积

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

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

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

示例 1:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

提示:

2 <= nums.length <= 105
-30 <= nums[i] <= 30
输入 保证 数组 answer[i] 在 32 位 整数范围内

在这里插入图片描述
直观实现(使用辅助数组)

public class ProductOfArrayExceptSelf {
   
    public int[] productExceptSelf(int[] nums) {
   
        int n = nums.length;
        if (n == 0) return new int[0];
        
        int[] leftProducts = new int[n];
        int[] rightProducts = new int[n];
        int[] result = new int[n];
        
        // 计算前缀乘积
        leftProducts[0] = 1;
        for (int i = 1; i < n; i++) {
   
            leftProducts[i] = leftProducts[i - 1] * nums[i - 1];
        }
        
        // 计算后缀乘积并同时计算结果
        int[] rightProducts = new int[n];
        rightProducts[n - 1] = 1; // 初始化最后一个元素的右侧乘积为1(因为右侧没有元素)
        for (int i = n - 2; i >= 0; i--) {
   
            rightProducts[i] = rightProducts[i + 1] * nums[i + 1];
        }
        
        for (int i = 0; i < n; i++) {
   
            result[i] = leftProducts[i] * rightProducts[i];
        }
        
        return result;
    }

    public static void main(String[] args) {
   
        ProductOfArrayExceptSelf solution = new ProductOfArrayExceptSelf();
        int[] nums = {
   1, 2, 3, 4};
        int[] result = solution.productExceptSelf(nums);
        
        for (int num : result) {
   
            System.out.print(num + " ");
        }
        // 输出: 24 12 8 6
    }
}

优化为常数空间
为了进一步优化空间复杂度,我们可以不使用额外的数组,而是通过两个变量来存储当前位置左侧和右侧的乘积:

public class ProductOfArrayExceptSelfOptimized {
   
    public int[] productExceptSelf(int[] nums) {
   
        int n = nums.length;
        int[] result = new int[n];
        
        // 初始化结果数组为1(因为乘法的单位元是1)
        int[] leftProducts = new int[n];
        int[] rightProducts = new int[n];
        int[] resultOptimized = new int[n]; // 或者直接在 resultOptimized 上操作,避免额外数组
        
        // 初始化左侧乘积数组(这里用一个变量代替数组来优化)
        int leftProduct = 1;
        for (int i = 0; i < n; i++) {
   
            resultOptimized[i] = leftProduct; // 先赋值当前结果为左侧乘积
            leftProduct *= nums[i]; // 更新左侧乘积(用于下一个位置)
        }
        
        // 从右向左计算右侧乘积并直接乘以左侧乘积
        int rightProduct = 1; // 右侧乘积初始化为1
        for (int i = n - 1; i >= 0; i--) {
   
            resultOptimized[i] *= rightProduct; // 先计算右侧乘积的累积
            rightProduct *= (i + 1 < n ? nums[i + 1] : 1; // 避免越界
        }
        
        // 或者使用两个变量在单次遍历中完成(更高效):
        int[] result = new int[nums.length];
        int leftProduct = 1;
        for (int i = 0; i < nums.length; i++) {
   
            result[i] = leftProduct;
            leftProduct *= nums[i]; // 在赋值后更新左侧乘积,用于下一个位置
        }
        
        // 重置左侧乘积为1从右向左计算(或直接在上述循环中从右向左结合右侧值)
        leftProduct = 1; // 重置为1用于从右向左计算
        for (int i = nums.length - 1; i >= 0; i--) {
   
            result[i] *= leftProduct; // 直接在原result数组上操作(或新建数组)
            leftProduct *= (i > 0 ? nums[i] : 1); // 避免i=0时nums[i]越界
        }
        
        return result; // 如果使用result数组,否则返回直接构建的结果数组
    }
    
    // 示例使用(展示如何结合第一个实现逻辑,但实际建议直接用上面优化后的单次遍历逻辑
    public static void main(String[] args) {
   
        ProductOfArrayExceptSelfOptimized solution = new ProductOfArrayExceptSelfOptimized();
        int[] nums = {
   1, 2, 3, 4};
        int[] result = solution.productExceptSelf(nums);
        
        for (int num : result) {
   
            System.out.print(num + " ");
        }
        // 输出: 24 12 8 6
    }
}

在这里插入图片描述

23.缺失的第一个正数

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

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

示例 1:

输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。
示例 2:

输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。
示例 3:

输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

提示:

1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1

在这里插入图片描述

public class FirstMissingPositive {
   
    public int firstMissingPositive(int[] nums) {
   
        int n = nums.length;
        
        // 将每个正整数 x 放置在索引 x-1 的位置上
        for (int i = 0; i < n; i++) {
   
            while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
   
                // 交换 nums[i] 和 nums[nums[i] - 1]
                int temp = nums[nums[i] - 1];
                nums[nums[i] - 1] = nums[i];
                nums[i] = temp;
            }
        }
        
        // 找到第一个位置 i 使得 nums[i] != i + 1
        for (int i = 0; i < n; i++) {
   
            if (nums[i] != i + 1) {
   
                return i + 1;
            }
        }
        
        // 如果所有位置都正确,则返回 n + 1
        return n + 1;
    }

    public static void main(String[] args) {
   
        FirstMissingPositive solution = new FirstMissingPositive();
        int[] nums1 = {
   1, 2, 0};
        int[] nums2 = {
   3, 4, -1, 1};
        int[] nums3 = {
   7, 8, 9, 11, 12};
        
        System.out.println(solution.firstMissingPositive(nums1)); // 输出: 3
        System.out.println(solution.firstMissingPositive(nums2)); // 输出: 2
        System.out.println(solution.firstMissingPositive(nums3)); // 输出: 1
    }
}

在这里插入图片描述

24.矩阵置零

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

示例 1:

在这里插入图片描述

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]
示例 2:
在这里插入图片描述

输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]

提示:

m == matrix.length
n == matrix[0].length
1 <= m, n <= 200
-231 <= matrix[i][j] <= 231 - 1

在这里插入图片描述

public class SetMatrixZeroes {
   
    public void setZeroes(int[][] matrix) {
   
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
   
            return;
        }
        
        int m = matrix.length;
        int n = matrix[0].length;
        
        boolean firstRowZero = false;
        boolean firstColZero = false;
        
        // 检查第一行是否有零
        for (int j = 0; j < n; j++) {
   
            if (matrix[0][j] == 0) {
   
                firstRowZero = true;
                break;
            }
        }
        
        // 检查第一列是否有零
        for (int i = 0; i < m; i++) {
   
            if (matrix[i][0] == 0) {
   
                firstColZero = true;
                break;
            }
        }
        
        // 使用第一行和第一列作为标记
        for (int i = 1; i < m; i++) {
   
            for (int j = 1; j < n; j++) {
   
                if (matrix[i][j] == 0) {
   
                    matrix[i][0] = 0;
                    matrix[0][j] = 0;
                }
            }
        }
        
        // 根据标记置零
        for (int i = 1; i < m; i++) {
   
            for (int j = 1; j < n; j++) {
   
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
   
                    matrix[i][j] = 0;
                }
            }
        }
        
        // 如果第一行需要置零
        if (firstRowZero) {
   
            for (int j = 0; j < n; j++) {
   
                matrix[0][j] = 0;
            }
        }
        
        // 如果第一列需要置零
        if (firstColZero) {
   
            for (int i = 0; i < m; i++) {
   
                matrix[i][0] = 0;
            }
        }
    }

    public static void main(String[] args) {
   
        SetMatrixZeroes solution = new SetMatrixZeroes();
        int[][] matrix = {
   
            {
   1, 1, 1},
            {
   1, 0, 1},
            {
   1, 1, 1}
        };
        
        solution.setZeroes(matrix);
        
        // 输出结果
        for (int i = 0; i < matrix.length; i++) {
   
            for (int j = 0; j < matrix[0].length; j++) {
   
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
    }
}

在这里插入图片描述

25.螺旋矩阵

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

示例 1:
在这里插入图片描述

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:

在这里插入图片描述

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

提示:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100

在这里插入图片描述

import java.util.ArrayList;
import java.util.List;

public class SpiralMatrix {
   
    public List<Integer> spiralOrder(int[][] matrix) {
   
        List<Integer> result = new ArrayList<>();
        
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
   
            return result;
        }
        
        int m = matrix.length;
        int n = matrix[0].length;
        
        int top = 0;
        int bottom = m - 1;
        int left = 0;
        int right = n - 1;
        
        while (top <= bottom && left <= right) {
   
            // 从左到右遍历
            for (int i = left; i <= right; i++) {
   
                result.add(matrix[top][i]);
            }
            top++;
            
            // 从上到下遍历
            for (int i = top; i <= bottom; i++) {
   
                result.add(matrix[i][right]);
            }
            right--;
            
            // 从右到左遍历(需要检查是否仍然在有效范围内)
            if (top <= bottom) {
   
                for (int i = right; i >= left; i--) {
   
                    result.add(matrix[bottom][i]);
                }
                bottom--;
            }
            
            // 从下到上遍历(需要检查是否仍然在有效范围内)
            if (left <= right) {
   
                for (int i = bottom; i >= top; i--) {
   
                    result.add(matrix[i][left]);
                }
                left++;
            }
        }
        
        return result;
    }

    public static void main(String[] args) {
   
        SpiralMatrix solution = new SpiralMatrix();
        in
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值