首页 > 编程语言 >算法解析-经典150(双指针、滑动窗口)

算法解析-经典150(双指针、滑动窗口)

时间:2025-01-03 22:31:28浏览次数:3  
标签:150 right nums int ++ 滑动 指针 public left

文章目录

双指针

1.验证回文串

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 125. 验证回文串
 *
 * @Author sun
 * @Create 2024/12/19 15:03
 * @Version 1.0
 */
public class t125 {

    public static boolean isPalindrome(String s) {
        // 直接双指针
        int left = 0;
        int right = s.length() - 1;
        while (left < right) {
            // 获取左右字符
            char leftChar = s.charAt(left);
            char rightChar = s.charAt(right);
            // 如果不是数字和字母,就直接下一位
            if (!Character.isDigit(leftChar) && !Character.isLetter(leftChar)) {
                left++;
                continue;
            }
            if (!Character.isDigit(rightChar) && !Character.isLetter(rightChar)) {
                right--;
                continue;
            }
            // 大写转换小写
            leftChar = Character.isUpperCase(leftChar) ? Character.toLowerCase(leftChar) : leftChar;
            rightChar = Character.isUpperCase(rightChar) ? Character.toLowerCase(rightChar) : rightChar;
            // 到这里左右都是数字字母字符了,可以直接比较
            if (leftChar != rightChar) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }

    public static void main(String[] args) {
        System.out.println("isPalindrome(\"A man, a plan, a canal: Panama\") = " + isPalindrome("A man, a plan, a canal: Panama"));
    }
}
2.思路

就是直接使用双指针,如果不是数字和字母,就直接下一位,大写转换小写,然后比较即可

2.判断子序列

1.动态规划解法
package com.sunxiansheng.classic150.g1;

/**
 * Description: 392. 判断子序列
 *
 * @Author sun
 * @Create 2024/12/21 09:36
 * @Version 1.0
 */
public class t392 {

    public boolean isSubsequence(String s, String t) {
        // dp[i][j]:以i-1,j-1为结尾的最长公共子序列的长度
        // 状态转移公式:相同时 dp[i][j] = dp[i - 1][j - 1] + 1 不同时 dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
        // 初始化
        int m = s.length();
        int n = t.length();
        int[][] dp = new int[m + 1][n + 1];
        int max = 0;
        // 填充dp数组
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s.charAt(i - 1) == t.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
                max = Math.max(max, dp[i][j]);
            }
        }
        return max ==  s.length();
    }
}
2.双指针
package com.sunxiansheng.classic150.g1;

/**
 * Description: 392. 判断子序列
 *
 * @Author sun
 * @Create 2024/12/21 09:36
 * @Version 1.0
 */
public class t392 {

    public static boolean isSubsequence(String s, String t) {
        // 双指针
        int left = 0;
        for (int right = 0; right < t.length(); right++) {
            // 如果左数组已经遍历完了就提前退出
            if (left >= s.length()) {
                break;
            }
            // 当右指针遇到了左指针指向的元素,左指针++
            if (t.charAt(right) == s.charAt(left)) {
                left++;
            }
        }
        return left >= s.length();
    }
}

3.两数之和 II - 输入有序数组

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 167. 两数之和 II - 输入有序数组
 *
 * @Author sun
 * @Create 2024/12/21 10:08
 * @Version 1.0
 */
public class t167 {

    public int[] twoSum(int[] numbers, int target) {
        // 两个指针从两边往中间移动
        int left = 0;
        int right = numbers.length - 1;
        while (left < right) {
            // 求和
            int sum = numbers[left] + numbers[right];
            // 如果大于target,就右指针移动,小于target就左指针移动
            if (sum > target) {
                right--;
            } else if (sum < target) {
                left++;
            } else {
                return new int[]{left + 1, right + 1};
            }
        }
        return null;
    }
}
2.思路

这里用的是贪心双指针,就是两个指针从两边往中间移动,和如果大于target,就右指针移动,小于target就左指针移动

4.盛最多水的容器

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 11. 盛最多水的容器
 *
 * @Author sun
 * @Create 2024/12/21 10:42
 * @Version 1.0
 */
public class t11 {

    public static int maxArea(int[] height) {
        int max = 0;
        int left = 0, right = height.length - 1;
        while (left < right) {
            // 求水量
            int water = Math.min(height[left], height[right]) * (right - left);
            // 更新最大值
            max = Math.max(max, water);
            // 哪边低移动哪边的指针
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return max;
    }
}
2.思路

使用贪心双指针解决

5.三数之和

1.答案
package com.sunxiansheng.classic150.g1;

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

/**
 * Description: 15. 三数之和
 *
 * @Author sun
 * @Create 2024/12/21 10:50
 * @Version 1.0
 */
public class t15 {

    public static List<List<Integer>> threeSum(int[] nums) {
        // 使用遍历+贪心双指针来解决
        List<List<Integer>> res = new ArrayList<>();
        // 如果数组长度小于3,直接返回空结果
        if (nums == null || nums.length < 3) {
            return res;
        }
        // 首先排序
        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[left] + nums[right] + nums[i];
                // 根据结果来贪心的调整状态
                if (sum > 0) {
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    res.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--;
                }
            }
        }
        return res;
    }
}
2.思路

使用遍历+贪心双指针来解决,注意两个关键点一个是在遍历时,遇到重复元素就跳过,另一个是在左右指针移动时,遇到重复元素也跳过

滑动窗口

1.长度最小的子数组

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 209. 长度最小的子数组
 *
 * @Author sun
 * @Create 2024/12/21 11:20
 * @Version 1.0
 */
public class t209 {

    public static int minSubArrayLen(int target, int[] nums) {
        // 滑动窗口定义:窗口内的元素要小于target
        int left = 0;
        int res = Integer.MAX_VALUE;
        int sum = 0;
        for (int right = 0; right < nums.length; right++) {
            // 加入窗口
            sum += nums[right];
            // 只要窗口内的元素是大于target的,就进行处理
            while (sum >= target) {
                // 计算结果
                res = Math.min(res, right - left + 1);
                // 滑动窗口
                sum -= nums[left++];
            }
        }
        return res == Integer.MAX_VALUE ? 0 : res;
    }
}
2.思路

先进行滑动窗口的定义窗口内的元素要小于target,那么只要窗口内的元素是大于target的,就进行处理,就可以计算结果了

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

1.答案
package com.sunxiansheng.classic150.g1;

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

/**
 * Description: 3. 无重复字符的最长子串
 *
 * @Author sun
 * @Create 2024/12/21 11:28
 * @Version 1.0
 */
public class t3 {

    public static int lengthOfLongestSubstring(String s) {
        // 滑动窗口定义:窗口内的元素不能重复
        int left = 0;
        int res = 0;
        Set<Character> set = new HashSet<>();
        for (int right = 0; right < s.length(); right++) {
            // 获取set的长度
            int before = set.size();
            // 加入窗口
            set.add(s.charAt(right));
            res = Math.max(res, set.size());
            // 当窗口内的元素重复时
            if (set.size() == before) {
                while (s.charAt(left) != s.charAt(right)) {
                    // 滑动窗口
                    set.remove(s.charAt(left));
                    left++;
                }
                // 到这里就说明当前left指向了那个重复元素,继续滑动窗口,不过不需要移动set
                left++;
            }
        }
        return res;
    }
}
2.思路

这里面比较复杂一点,十分考察对滑动窗口算法的理解,加入窗口前先要获取一下加入set之前的长度,如果加入窗口后长度不变,那么就一定是元素重复了,需要滑动窗口直到不重复。计算结果则是在加入窗口的时候

3.最小覆盖子串

1.答案
package com.sunxiansheng.classic150.g1;

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

/**
 * Description: 76. 最小覆盖子串
 *
 * @Author sun
 * @Create 2024/12/21 14:19
 * @Version 1.0
 */
public class t76 {

    public static String minWindow(String s, String t) {
        // 滑动窗口定义:窗口内元素不能全部包含t的所有元素
        // 使用map来记录t的词频
        Map<Character, Integer> tMap = new HashMap<>();
        for (int i = 0; i < t.length(); i++) {
            tMap.put(t.charAt(i), tMap.getOrDefault(t.charAt(i), 0) + 1);
        }
        // 所有的字符数(当满足条件的字符数为n时,表明该子串就是覆盖子串了)
        int n = t.length();
        // 满足条件的字符数
        int satisfy = 0;
        // s的词频
        Map<Character, Integer> sMap = new HashMap<>();
        // 最小覆盖子串的长度
        int minSonLength = Integer.MAX_VALUE;
        // 最小覆盖子串
        String minSon = "";
        int left = 0;
        for (int right = 0; right < s.length(); right++) {
            // 获取到目标字符
            char c = s.charAt(right);
            // 只考虑是t的元素的情况
            if (tMap.containsKey(c)) {
                // 加入窗口
                sMap.put(c, sMap.getOrDefault(c, 0) + 1);
                // 如果目标字符的出现次数是小于t中的频率,就可以增加满足条件的字符数
                if (sMap.get(c) <= tMap.get(c)) {
                    satisfy++;
                }
            }
            // 只要目前是覆盖子串,就需要滑动窗口
            while (satisfy == n) {
                // 更新最小覆盖子串
                if ((right - left + 1) < minSonLength) {
                    minSon = s.substring(left, right + 1);
                    minSonLength = right - left + 1;
                }
                // 滑动窗口
                // 当是t的元素时
                if (tMap.containsKey(s.charAt(left))) {
                    // 更新词频和满足的字符数
                    sMap.put(s.charAt(left), sMap.get(s.charAt(left)) - 1);
                    // 只有当窗口中的元素减少后,确实比t中的词频少了,才会减少满足的字符数
                    if (sMap.get(s.charAt(left)) < tMap.get(s.charAt(left))) {
                        satisfy--;
                    }
                }
                // 不是t的元素就直接滑动即可
                left++;
            }
        }
        return minSon;
    }

    public static void main(String[] args) {
        minWindow("ADOBECODEBANC", "ABC");
    }
}
2.思路

核心就是使用Map来记录两个字符串的词频,然后当满足条件的字符数量为t的个数时就是覆盖子串

标签:150,right,nums,int,++,滑动,指针,public,left
From: https://blog.csdn.net/m0_64637029/article/details/144919233

相关文章

  • 算法解析-经典150(矩阵、哈希表)
    文章目录矩阵1.有效的数独1.答案2.思路2.螺旋矩阵1.答案2.思路3.旋转图像1.答案2.思路4.矩阵置零1.答案2.思路哈希表1.赎金信1.答案2.思路2.同构字符串1.答案2.思路3.单词规律1.答案2.思路4.有效的字母异位词1.答案2.思路5.字母异位词分组1.答案2.思路......
  • C++中的数组与指针
    在大多数C++书籍或教程中,数组和指针的知识总是放在一起让大家学习,这是为什么,它们之间有什么联系呢?在C++中,数组与指针有着紧密的联系,主要体现在下面几个方面:1、数组名即指针:本质联系:在大多数情况下,数组名会被隐式转换为指向数组第一个元素的指针。例如,对于一个数组intarr[5];......
  • C语言指针
    一、指针的基本概念 1. 定义 -指针是C语言中的一个重要概念,它是一个变量,其值为另一个变量的地址。简单来说,指针“指向”了内存中的某个位置,这个位置存放着其他变量的值。-例如:cinta=10;int*p;//声明一个指向int类型的指针p=&a;//将指针p指向变量a的地......
  • C中如何理解指针和引用的区别?
    在C语言中,指针和引用是两个重要的概念,它们都与内存地址和变量之间的关系有关,但它们在定义、使用和特性上存在显著的区别。下面将详细解释指针和引用的区别,并通过示例代码进行说明。指针的基本概念指针是一种变量,其值为另一个变量的地址,即内存位置。通过使用星号(*)声明指针变量......
  • 单词背诵(滑动窗口)
    题目链接:https://www.luogu.com.cn/problem/P1381题意:分别给你两个字符串的序列t和序列s,要求你输出在序列s与序列t有多少个相同的字符串,以及相同字符串子串的最小长度思路:类似于最小覆盖子串问题滑动窗口+简单哈希通过map来存储,序列t中出现的字符串在map中-1,当成欠款,窗口右......
  • 使用指针操作Jobs示例
    用指针传入Jobs操作对于外部类型为传统数据类型的集合来说效率是比较高的,以下是示例代码: usingSystem;usingSystem.Runtime.InteropServices;usingUnity.Jobs;usingUnity.Burst;usingUnity.Collections.LowLevel.Unsafe;usingUnityEngine;publicclassTestClass......
  • 206翻转指针
    使用双指针法,注意一下更新左右指针的顺序就好了。这里还要注意一下终止条件,画个图就行了。leetcode里面头节点就是第一个存储数据的节点,没有虚拟头节点classSolution{public:ListNode*reverseList(ListNode*head){ListNode*left;ListNode*right;......
  • WPF DevExpress按住鼠标下拉滑动列表功能
    usingSystem;usingSystem.Windows;usingSystem.Windows.Controls;usingSystem.Windows.Input;usingSystem.Windows.Media;usingSystem.Windows.Threading;usingDevExpress.Xpf.Grid;namespaceClient{publicclassAutoScrollHelper{publicA......
  • 2025/1/2 【双指针法】LeetCode27.移除元素 【√】 ❗未完结❗
    27.移除元素-力扣(LeetCode)代码随想录数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。Myanswer:快慢指针法classSolution:defremoveElement(self,nums:List[int],val:int)->int:n=len(nums)j=0forii......
  • 【优选算法】查找总价格为目标值的两个商品(双指针)
    算法_云边有个稻草人的博客-CSDN博客目录解法一:暴力算法解法二:双指针(时间复杂度为O(N))【代码编写】LCR179.查找总价格为目标值的两个商品-力扣(LeetCode)解法一:暴力算法用两个for循环,列出所有的两个数的和进行判断,时间复杂度为O(N^2),不推荐。算法流程:两层......