(算法题)Day-20230301

环形链表 II

思路快慢指针

根据,设起点到入点为a,圈为b个节点

  1. f=2s (快指针每次2步,路程刚好2倍)
  2. f = s + nb (首次相遇时,刚好多走了n圈

推出:s = nb,即快指针比慢指针在圈里多走了n圈,这样只需要再走a步即可到达入点

从head结点走到入环点需要走 : a + nb; 而slow已经走了nb,那么slow再走a步就是入环点了

所以如何知道slow刚好走了a步? 从head开始,和slow指针一起走,再次相遇时刚好就是a步

public class Solution {
    public ListNode detectCycle(ListNode 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;
    }
}

无重复字符的最长子串

3. 无重复字符的最长子串 - powcai题解

思路滑动窗口 用 left变量 保存最新重复字符的索引位置,方便下次移动;利用 max 变量 保存子字符串最大长度

利用一个map来保存出现字符的索引,如果新加入的字符在map中已经出现,那么就更新 left,这样就可以通过当前索引和 left 相减来更新 max

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length()==0) return 0;
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        int max = 0;
        int left = 0;
        for(int i = 0; i < s.length(); i ++){
            if(map.containsKey(s.charAt(i))){
                left = Math.max(left,map.get(s.charAt(i)) + 1);
            }
            map.put(s.charAt(i),i);
            max = Math.max(max,i-left+1);
        }
        return max;
        
    }
}

滑动窗口题目

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

30. 串联所有单词的子串

76. 最小覆盖子串

159. 至多包含两个不同字符的最长子串

209. 长度最小的子数组

239. 滑动窗口最大值

567. 字符串的排列

632. 最小区间

727. 最小窗口子序列


最长回文子串

思路动态规划

从 i 到 j 的子字符串,只有当 i+1 到 j-1 的子字符串为回文串 且 \(S_i==S_j\) 时,i 到 j 的子字符串才为回文串

要注意边界情况,即一个字符或两个字符的情况

public class Solution {

    public String longestPalindrome(String s) {
        int len = s.length();
        if (len < 2) {
            return s;
        }

        int maxLen = 1;
        int begin = 0;
        // dp[i][j] 表示 s[i..j] 是否是回文串
        boolean[][] dp = new boolean[len][len];
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }

        char[] charArray = s.toCharArray();
        // 递推开始
        // 先枚举子串长度
        for (int L = 2; L <= len; L++) {
            // 枚举左边界,左边界的上限设置可以宽松一些
            for (int i = 0; i < len; i++) {
                // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                int j = L + i - 1;
                // 如果右边界越界,就可以退出当前循环
                if (j >= len) {
                    break;
                }

                if (charArray[i] != charArray[j]) {
                    dp[i][j] = false;
                } else {
                    if (j - i < 3) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                // 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substring(begin, begin + maxLen);
    }
}

盛最多水的容器

思路双指针,由两侧向内 满足条件时 收缩,收缩时只有当 高度增大时 面积才有可能增大

class Solution {
    public int maxArea(int[] height) {
        int i = 0, j = height.length - 1, res = 0;
        while(i < j) {
            res = height[i] < height[j] ? 
                Math.max(res, (j - i) * height[i++]):  		// 注Math.max(res, height[i++]*(j - i)) 过不了
                Math.max(res, (j - i) * height[j--]); 		// 注Math.max(res, height[j--]*(j - i))过不了
        }
        return res;
    }
}

三数之和

思路:先对数组进行排序,然后固定一个数,然后利用双指针从两侧向内走,进行判断 更新相应指针

注意过滤掉重复的数,即如果重复则跳过

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        for(int k = 0; k < nums.length - 2; k++){
            if(nums[k] > 0) break;
            if(k > 0 && nums[k] == nums[k - 1]) continue;
            int i = k + 1, j = nums.length - 1;
            while(i < j){
                int sum = nums[k] + nums[i] + nums[j];
                if(sum < 0){
                    while(i < j && nums[i] == nums[++i]);
                } else if (sum > 0) {
                    while(i < j && nums[j] == nums[--j]);
                } else {
                    res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
                    while(i < j && nums[i] == nums[++i]);
                    while(i < j && nums[j] == nums[--j]);
                }
            }
        }
        return res;
    }
}
posted @ 2023-03-05 14:06  黄一洋  阅读(2)  评论(0)    收藏  举报