面试经典150题0427

面试经典150题0427

Leetcode011 盛最多水的容器

**双指针:**左指针指向左边界,右指针指向右边界;计算当前情况下的容积。然后比较左指针高度height[left]和右指针高度height[right],哪一边的小就移动对应的指针。

public static int maxArea(int[] height){
    int left = 0, right = height.length - 1;
    int area = 0;
    int areaMax = 0;
    while (left < right){

        area = Math.min(height[left], height[right]) * (right - left);
        areaMax = Math.max(area, areaMax);
        if(height[left] < height[right]){
            left++;
        }
        else {
            right--;
        }
    }
    return areaMax;
}
Leetcode015 三数之和

双指针:

  1. 先将数组排序
  2. 然后固定一个元素位置i,该元素从[0, nums.length - 2)遍历,为了避免重复答案,需要判断当前元素是否和前一个元素相同,相同则继续移动;
  3. 否则,则指定左指针为i+1,右指针为数组右边界。
  4. 两个元素相加和-nums[i]进行比较,大于则移动右指针,小于则移动左指针。
  5. 当找到答案时,将答案添加到链表中,左右指针同时移动。
  6. 同时为了避免答案重复,需要比较nums[left]==nums[left-1],相等则继续移动左指针;否则继续重复第4步。
public static List<List<Integer>> threeSum(int[] nums){
    List<List<Integer>> res = new ArrayList<>();
    Arrays.sort(nums);
    for(int i = 0; i < nums.length - 2; i++){
        while (i > 0 && i < nums.length - 2 && nums[i] == nums[i-1]){
            i++;
        }
        int left = i + 1;
        int right = nums.length - 1;
        int target = -nums[i];
        while (left < right){
            if(nums[left] + nums[right] == target){
                List<Integer> tmp = new ArrayList<>();
                tmp.add(nums[i]);
                tmp.add(nums[left]);
                tmp.add(nums[right]);
                res.add(tmp);
                left++;
                right--;
                while (left < right && nums[left] == nums[left - 1]){
                    left++;
                }
            }
            else if(nums[left] + nums[right] < target){
                left++;
            }
            else {
                right--;
            }
        }
    }
    return res;
}
Leetcode209 长度最小的子数组

滑动窗口:

  • 使用两个指针维护一个滑动窗口,设置一个变量sum记录滑动窗口内的和
  • sum>=target时,尝试更新窗口最小值minLen,并移动左指针。
  • sum<target时,移动右指针。
public static int minSubArrayLen(int target, int[] nums){
    int left = 0, right = 0;
    int sum = 0;
    int minLen = Integer.MAX_VALUE;
    while (right < nums.length){
        sum += nums[right++];
        while (sum >= target){
            minLen = Math.min(minLen, right - left);
            sum -= nums[left++];
        }
    }
    return minLen == Integer.MAX_VALUE ? 0 : minLen;
}
Leetcode003 无重复字符的最长字串
  • 双指针维护一个动态窗口,窗口中的内容为无重复的字串。
  • 同时使用一个数组记录窗口中每个字符出现次数。
  • 右指针移动增大窗口时,如果该字符在窗口内出现,则移动左指针,同时更改数组对应字符记录;如果字符没有在窗口内出现,则更新数组内对应字符记录。
  • 数组可以设置为大小128,用来统计ASCII中字符。由题意可知s由英文字母、数字、符号和空格组成。
public static int lengthOfLongestSubstring(String s){
    int[] array = new int[128];
    int left = 0, right = 0;
    int maxLen = 0;
    while (right < s.length()){
        while (right < s.length() && array[s.charAt(right)] == 0){
            array[s.charAt(right++)]++;
            maxLen = Math.max(maxLen, right - left);
        }
        array[s.charAt(left++)]--;
    }
    return maxLen;
}
Leetcode030 串联所有单词的子串

判断每个子串是否符合条件,子串的长度为words.length * words[0].length(),符合的话就把下标保存起来。

image-20240427203054777

如何判断子串包含所有单词?

  • 首先,将所有的单词存放到HashMap中,key存储单词,value存放单词出现的个数(因为给出的单词可能重复,其value可能为1或者2等)。

  • 然后扫描子串中的单词,如果当前扫描的单词在之前的HashMap中,就把该单词存放到新的HashMap中,并判断新的HashMap中该单词的value是否大于之前单词的value

    • 如果大于,说明该子串不是我们要找的,接着判断下一个子串即可。

    • 如果不大于,接着判断下一个单词的情况。

  • 如果当前扫描单词不在之前的HashMap中,则直接判断下一个子串。

  • 当子串扫描结束时,如果子串的全部单词都符合,那么该子串就是要找的其中一个。

public static List<Integer> findSubstring(String s, String[] words) {
    Map<String, Integer> map = new HashMap<>();
    List<Integer> res = new ArrayList<>();
    for(String tmp : words){
        map.put(tmp, map.getOrDefault(tmp,0)+1);
    }
    int wordLen = words[0].length();
    int rightBound = s.length() - words.length * wordLen + 1;
    for(int i = 0; i < rightBound; i++){
        int start = i;
        int end = wordLen + i;
        int cnt = 0;
        Map<String, Integer> tmpMap = new HashMap<>();
        while (cnt < words.length){
            String sub = s.substring(start, end);
            if(map.containsKey(sub)){
                // sub为words中的单词,判断已经出现的次数是否超过words中相同单词的次数
                int subValue = tmpMap.getOrDefault(sub, 0) + 1;
                if(subValue > map.get(sub)){
                    break;
                }
                tmpMap.put(sub, subValue);
            }
            else {
                break;
            }
            start += wordLen;
            end += wordLen;
            cnt++;
        }
        if(cnt == words.length){
            // 子串满足条件,由words中的单词组成
            res.add(i);
        }
    }
    return res;
}
Leetcode076 最小覆盖子串

滑动窗口

  • 初始化ansLeft = -1ansRight = m,分别记录最短子串的左右端点,其中m为字符串s的长度。
  • 使用一个数组arrayT统计``t中每个字符出现的次数,由于包含大小写,可以定义数组的大小为128.
  • 初始化left为0以及一个数组arrayS,用来统计s子串中每个字母的出现次数。
  • 初始化less为字符串t中不同字母的个数
  • 遍历s,设当前枚举的子串右端点为right(右指针),把c = s[right]出现的次数加一。如果arrayS[c] == arrayT[c],说明字符c的出现次数在窗口内已经满足,这是将less--
  • 如果less=0,说明arrayS中的每个字母的出现次数都大于等于arrayT中字母的出现次数。
    • 如果right - left < ansRight - ansLeft,则更新ansLeft = left, ansRight = right.
    • 把字母x = s[left]的出现次数减一。减一前,如果arrayS[x] = arrayT[x],说明左指针left移动后x的出现次数不满足要求,需要把less++arrayS[x] > arrayT[X],把left再右移。
    • 重复步骤,直到less > 0,然后移动右指针right增大窗口大小。
  • 最后,如果ansLeft < 0,说明没有找到符合要求的子串,返回空字符串;否则返回下标ansLeft到下标ansRight之间的子串。
public static String minWindow(String s, String t){
    int[] arrayS = new int[128];
    int[] arrayT = new int[128];
    int ansLeft = -1, ansRight = s.length();
    int less = 0;
    for (int i = 0; i < t.length(); i++) {
        if(arrayT[t.charAt(i)]++ == 0){
            less++;
        }
    }
    int left = 0, right = 0;
    while (right < s.length()){
        char c = s.charAt(right++);
        // 字符c在窗口中出现的次数等于在字符串t中出现的次数,less--
        if(arrayT[c] == ++arrayS[c]){
            less--;
        }
        while (less == 0){
            if(right - left < ansRight - ansLeft){
                ansLeft = left;
                ansRight = right;
            }
            char x = s.charAt(left++);
            if(arrayS[x]-- == arrayT[x]){
                // 字符x在窗口中出现的次数已经小于在字符串t中的次数,less++
                less++;
            }
        }
    }
    return  ansLeft == -1 ? "" : s.substring(ansLeft, ansRight);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值