算法-双指针(java)

左右指针

leetcode 15. 三数之和

算法-双指针(java)_第1张图片

解题:

  • 首先对数组进行排序,排序后固定一个数nums[i],再使用左右指针指向nums[i]后面的两端,数字分别为nums[ L]和nums[R],计算三个数的和sum判断是否满足为0,满足则添加进结果集
  • 如果nums[i]大于0,则三数之和必然无法等于0,结束循环
  • 如果nums[i]==nums[i-1],则说明该数字重复,会导致结果重复,所以应该跳过
  • 当sum==0时,nums[L]==nums[L-1]则会导致结果重复,应该跳过,L++
  • 当 sum ==0时,nums[R]==nums[R-1]则会导致结果重复,应该跳过,R–
  • 时间复杂度:0(n2),n为数组长度
public List<List<Integer>> threeSum(int[] nums) {
     
        // 排序
        Arrays.sort(nums);
        ArrayList<List<Integer>> ans = new ArrayList<>();
        // 双指针
        for (int i = 0; i < nums.length; i++) {
     
            // 满足条件提前结束
            if (nums[i]>0) break;
            if (i!=0&&nums[i]==nums[i-1]){
     
                continue;
            }
            int L = i+1;
            int R = nums.length-1;
            while (L<R){
     
                int sum = nums[i]+nums[L]+nums[R];
                if (sum == 0 ){
     
                    ans.add(Arrays.asList(new Integer[]{
     nums[i],nums[L],nums[R]}));
                    while (L<R && nums[L]==nums[++L]){
     } //去重
                    while (L<R && nums[R]==nums[--R]){
     } //去重
                }else if(sum<0){
     
                    while (L<R && nums[L]==nums[++L]){
     }
                }else {
     
                    while (L<R && nums[R]==nums[--R]){
     }
                }
            }
        }
        return ans;
    }

参考:

  • 常用的双指针技巧

滑动窗口

76. 最小覆盖子串

算法-双指针(java)_第2张图片
滑动窗口框架

public String minWindow(String s, String t){
     
        //目标计数
        HashMap<Character, Integer> target = new HashMap<>();
        //目标统计
        ……
        //滑动框内计数
        HashMap<Character, Integer> window = new HashMap<>();
        // 左右指针,左闭右开
        int L = 0, R = 0;
        while (R < s.length()){
     
            // 移入的元素
            Character cr = s.charAt(R);
            // 增大窗口
            R++;
            // 窗口内数据更新
            ……            
            // 只要窗口满足条件,就收缩
            while(窗口满足条件){
     
                //更新结果
                ……
                // 移出左边的元素
                Character cl = s.charAt(L);
                //缩小窗口,L++;
                L++;
                // 窗口内数据更新
            	……
            }
        }
        return 结果
    }

完整代码

public String minWindow(String s, String t){
     
        //目标计数
        HashMap<Character, Integer> target = new HashMap<>();
        //目标统计
        for(Character c:t.toCharArray()){
     
            target.put(c,target.getOrDefault(c,0)+1);
        }
        //滑动框内计数
        HashMap<Character, Integer> window = new HashMap<>();
        // 左右指针,左闭右开
        int L = 0, R = 0;
        // 达成目标个数累计(如果==target.size()表示window框包含所有目标中内容)
        int cnt=0;
        // 记录最小覆盖子串起始索引、长度
        int start = 0,res_len = Integer.MAX_VALUE;
        while (R < s.length()){
     
            // 移入的元素
            Character cr = s.charAt(R);
            // 增大窗口
            R++;
            // 判断移入的元素是否是target中元素
            if(target.containsKey(cr)){
     
                //增加window对应元素数量,注意window中可能没有cr Key,所以用getOrDefault
                window.put(cr,window.getOrDefault(cr,0)+1);
                // 加入元素可能使窗口接近目标,增加cnt计数
                if (window.get(cr).equals(target.get(cr))) cnt++;

            }
            // 只要窗口满足条件,就收缩
            while(cnt == target.size()){
     
                //更新结果
                if (R-L <res_len){
     
                    start = L;
                    res_len = R-L;
                }
                // 移出左边的元素
                Character cl = s.charAt(L);
                //缩小窗口,L++;
                L++;
                // 判断移出的元素是否和t中相同
                if(target.containsKey(cl)){
     
                    // 移出元素可能使窗口不满足条件
                    if (window.get(cl).equals(target.get(cl))) cnt--;
                    //减小window对应元素数量
                    window.put(cl,window.get(cl)-1);
                }
            }

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

参考:labuladong的算法小抄

567. 字符串的排列

算法-双指针(java)_第3张图片
解答:

public boolean checkInclusion(String s1, String s2) {
     
        HashMap<Character, Integer> target = new HashMap<>();
        for (Character c : s1.toCharArray()){
     
            target.put(c,target.getOrDefault(c,0)+1);
        }
        HashMap<Character, Integer> window = new HashMap<>();
        int L=0,R=0;
        int cnt = 0;
        while (R <s2.length()){
     
            Character cr = s2.charAt(R);
            R++;
            // 进⾏窗⼝内数据的⼀系列更新
            if (target.containsKey(cr)){
     
                window.put(cr,window.getOrDefault(cr,0)+1);
                if (window.get(cr).equals(target.get(cr))) cnt++;
            }

            // 判断左侧窗⼝是否要收缩
            while (R - L >=s1.length()){
     
                // 在这⾥判断是否找到了合法的⼦串
                if (cnt == target.size()) return true;
                Character cl = s2.charAt(L);
                L++;
                // 进⾏窗⼝内数据的⼀系列更新
                if (target.containsKey(cl)){
     
                    if (window.get(cl).equals(target.get(cl))) cnt--;
                    window.put(cl,window.get(cl)-1);
                }
            }
        }
        // 未找到符合条件的⼦串
        return false;
    }
438. 找到字符串中所有字母异位词

算法-双指针(java)_第4张图片
解答:

public List<Integer> findAnagrams(String s, String p){
     
        HashMap<Character, Integer> target = new HashMap<>();
        for(Character c:p.toCharArray()){
     
            target.put(c,target.getOrDefault(c,0)+1);
        }
        HashMap<Character, Integer> window = new HashMap<>();
        ArrayList<Integer> res = new ArrayList<>();
        int L=0,R=0;
        int cnt=0;
        while (R <s.length()){
     
            Character cr = s.charAt(R);
            R++;
            if (target.containsKey(cr)){
     
                window.put(cr,window.getOrDefault(cr,0)+1);
                if (window.get(cr).equals(target.get(cr))) cnt++;
            }
            // 判断左侧窗⼝是否要收缩
            while (R-L >= p.length() ){
     
                Character cl = s.charAt(L);
                if (cnt == target.size()){
     
                    res.add(L);
                }
                L++;
                if (target.containsKey(cl)){
     
                    if (window.get(cl).equals(target.get(cl))) cnt--;
                    window.put(cl,window.get(cl)-1);
                }
            }
        }
        return res;
    }
3. 无重复字符的最长子串

算法-双指针(java)_第5张图片
解答

// 滑动窗口 11ms
    public int lengthOfLongestSubstring(String s){
     
        HashMap<Character, Integer> map = new HashMap<>();
        int L = 0,R = 0 ;
        int res = Integer.MIN_VALUE;
        while (R < s.length()){
     
            Character cr = s.charAt(R);
            R++;
            map.put(cr,map.getOrDefault(cr,0)+1);
            // 当 map.get(cr) 值⼤于 1 时,说明窗⼝中存在重复字符,不符合条件,就该L++缩⼩窗⼝
            while (map.get(cr)>1){
     
                Character cl = s.charAt(L);
                L++;
                map.put(cl,map.get(cl)-1);
            }
            // 更新无重复的 最长字串
            if (R - L >res ) res = R-L;
        }
        return res==Integer.MIN_VALUE?0:res;
    }

快慢指针

你可能感兴趣的:(算法,算法)