双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水

目录

  • 一、合并两个有序数组
    • 1.1 题目
    • 1.3 题解
  • 二、判断是否为回文字符串
    • 2.1 题目
    • 2.2 题解
  • 三、合并区间(排序+贪心)
    • 3.1 题目
    • 3.2 题解
  • 四、最小覆盖子串(哈希表+滑动窗口)
    • 4.1 题目
    • 4.2 题解
  • 五、最长无重复子数组(滑动窗口)
    • 5.1 题目
    • 5.2 题解
  • 六、盛水最多的容器(双指针+贪心)
    • 6.1 题目
    • 6.2 题解
  • 七、接雨水(一维)
    • 7.1 题目
    • 7.2 题解

一、合并两个有序数组

1.1 题目

双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第1张图片

1.3 题解

思路:题目中有说A数组的长度为A中初始元素个数和B中初始元素之和,因此我们不必建立新的数组,直接将比较后的元素放到A中即可,但有一点需要注意,不能按照常规思路,定义两个指针分别指向两个数组的头,将较小的元素放到A数组中,这样做是有可能将A中原有的元素覆盖掉的,因此我们需要从两个数组的末尾开始比较,每次都是将较大的元素放到A数组的末尾。
双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第2张图片
具体步骤:

step1:定义i指向A中最后一个有效元素,j指向B中最后一个元素,k指向A数组的最后一个位置
step2:比较i,j所指向的元素,将较大者放到k的位置,k–,如果i指向的元素大,i–,否则j–,直到i或j小于0时跳出循环
step3:将A或B中的剩余元素拷贝到A中

代码:

   public void merge(int A[], int m, int B[], int n) { 
       int i=m-1;
       int j=n-1;
       int k=m+n-1;
       while(j>=0 && i>=0){
          if(A[i]>=B[j]){
              A[k]=A[i];
              i--;
              k--;
          }else {
              A[k]=B[j];
              j--;
              k--;
          }
       }
       while(i>=0){
           A[k--]=A[i--];
       }
        while(j>=0){
            A[k--]=B[j--];
        }
    }

二、判断是否为回文字符串

2.1 题目

双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第3张图片

2.2 题解

思路回文子串的特点,正向遍历的结果和逆向遍历的结果是一样的,因此我们可以准备两个指针,分别用于正向遍历和逆向遍历

具体步骤:
step1:定义两个指针,分别指向头和尾
step2:头指针往后走,尾指针往前走,比较头尾指针指向的元素,如果不想等,直接返回不是回文子串
step3:直到头尾指针相遇,都一致就是回文子串

代码:

   public boolean judge (String str) {
       if(str==null) return true;
        char[] s=str.toCharArray();
        int l=0;
        int r=s.length-1;
        while(l<r){
            if(s[l]==s[r]){
                l++;
                r--;
            }else {
                return false;
            }
        }
        return true;
    }

三、合并区间(排序+贪心)

3.1 题目

双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第4张图片

3.2 题解

贪心思想:找出整体当中给的每个局部子结构的最优解,并且最终将所有的这些局部最优解结合起来形成整体上的一个最优解

思路只有重合的区间才是能够合并的区间,也就是后一个区间的start大于前一个区间的end,此时两个区间可以进行合并,因此我们可以根据每个区间的start对所有的区间进行排序

具体步骤:

step1:根据start对所有区间排序
step2:将排序后的第一个区间加入到res中
step3:遍历后续的区间,如果当前遍历到的区间的start小于res中最后一个区间的end,说明此时该区间可以和res中的最后一个区间进行合并,合并后新区间的end为两者end的最大值
step4:如果如果当前遍历到的区间的start大于res中最后一个区间的end,说明当前遍历到的区间无法和res中末尾的区间合并,count++

代码:

  public ArrayList<Interval> merge(ArrayList<Interval> intervals) {

        ArrayList<Interval> ret=new ArrayList<>();
        if(intervals==null || intervals.size()==0 return ret;
        Collections.sort(intervals, new Comparator<Interval>() {
            @Override
            public int compare(Interval o1, Interval o2) {
                if(o1.start!=o2.start){
                    return o1.start-o2.start;
                }else {
                    return o1.end-o2.end;
                }
            }
        });
       ret.add(intervals.get(0));
       int count=0;
       for(int i=1;i<intervals.size();i++){
           Interval tmp=intervals.get(i);
           if(tmp.start<= ret.get(count).end){
               if(tmp.end>ret.get(count).end){
                   ret.get(count).end=tmp.end;
               }
           }else {
               count++;
               Interval val=new Interval(tmp.start,tmp.end);
               ret.add(tmp);
           }
       }
       return ret;
    }

【补充】: Collections.sort()方法
双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第5张图片

四、最小覆盖子串(哈希表+滑动窗口)

4.1 题目

双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第6张图片

4.2 题解

思路创建一个哈希表,key值为字符串T中的字符,通过遍历T对哈希表初始化,每遍历到一个字符,就将其对应的value-1,后面遍历S字符串时,每遍历到一个字符,就将该字符对应的value+1,使用双指针维护滑动窗口,在该窗口内,如果哈希表中所有的value都大于等于0,说明该窗口所覆盖的字符串包含T中所有字符,此时我们可以移动左指针,缩小窗口,找到最小的覆盖子串

具体步骤:

step1:遍历T字符串初始化哈希表
step2:slow和fast为双指针,用来滑动窗口,left和right用来维护最小窗口的左右两个边界,用于最后截取字符串
step3:fast后移,遍历S字符串,每遍历到一个字符,就将该字符对应的value+1,在该窗口内,如果哈希表中所有的value都大于等于0,明该窗口所覆盖的字符串包含T中所有字符,此时我们可以移动左指针,缩小窗口,找到最小的覆盖子串
step4:当不满足哈希表中所有的value都大于等于0时,fast继续后移
step5:判断left(初始化为-1)是否为-1,如果是,说明没找到 step6:通过left和right截取最小覆盖字串

代码:

 public boolean check(int[] hash){
         for(int i=0;i<hash.length;i++){
             if(hash[i]<0){
                 return false;
             }
         }
         return true;
     }
    public String minWindow (String S, String T) {
       int[] hash=new int[128];
       for(int i=0;i<T.length();i++){
           hash[T.charAt(i)]-=1;
       }
      int left=-1;
      int right=-1;
      int slow=0;
      int fast=0;
      int cnt=Integer.MAX_VALUE;
      for(;fast<S.length();fast++){
        char ch=S.charAt(fast);
        hash[ch]++;
        while(check(hash)){
          if(fast-slow+1<cnt){
              cnt=fast-slow+1;
              left=slow;
              right=fast;
          }
          hash[S.charAt(slow)]--;
          slow++;
        }
      }
      if(left==-1) return "";
      return S.substring(left,right+1);
    }

五、最长无重复子数组(滑动窗口)

5.1 题目

双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第7张图片

5.2 题解

思路维护一个滑动窗口,使窗口中的元素都是不重复的,返回最大的窗口大小

具体步骤:

step1:创建一个哈希表,用来存储当前窗口中含有的元素和其对应的下标,每当遍历到一个元素时,可以通过哈希表快速判断该元素是否存在于窗口中
step2:left指向窗口的左边界,right指向窗口的右边界,使用right用来遍历数组,如果遍历到的元素不在窗口中那么将该元素加入到窗口中,并更新窗口的大小
step3:如果当前遍历到的元素存在于窗口中,则要移动left,让left指向该元素上一次出现的位置的下一个位置,并在left移动的过程中,将不在窗口中的元素从哈希表中移除

图示:
双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第8张图片

代码:

 public int maxLength (int[] arr) {
        int max=0;
        int left=0;
        HashMap<Integer,Integer> map=new HashMap<>();
        for(int right=0;right<arr.length;right++){
            Integer tmp=map.get(arr[right]);
            if(tmp==null){
                map.put(arr[right],right);
                max=Math.max(max,right-left+1);
            }else {
                while (left<=tmp){
                    map.remove(arr[left]);
                    left++;
                }
                map.put(arr[right],right);
            }
        }
        return max;
    }

六、盛水最多的容器(双指针+贪心)

6.1 题目

双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第9张图片

6.2 题解

思路:

容器的容量与较短的一条边和容器的底有关,初始情况下,两个指针分别指向首尾位置,此时容器的底最长,但与较短的边长的乘积不一定是最大的,可能出现边长更高,底较短,但乘积更大的情况,因此我们使用对撞指针向中间靠拢,再靠拢的过程中底边是不短缩短的,要想得到更大的体积,就要让边长更长,因此每次都让较短的边像中间靠

具体步骤:

step1:定义L和R,分别指向首尾位置
step2:更新容器的最大值
step3:每次都让较短的边向中间靠

代码:

  public int maxArea (int[] height) {
        if(height==null || height.length<2) return 0;
      int l=0;
      int r=height.length-1;
      int ans=0;
      while(l<r){
          
          ans=Math.max(ans,Math.min(height[l],height[r])*(r-l));
          if(height[l]<height[r]){
              l++;
          }else {
              r--;
          }
      }
      return ans;
    }

七、接雨水(一维)

7.1 题目

双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第10张图片

7.2 题解

思路
双指针:合并有序数组、回文字符串、合并区间、最小覆盖字串、最长无重复子数组、盛水最多的容器、接雨水_第11张图片

代码

public long maxWater (int[] arr) {
        if(arr==null||arr.length<2){
            return 0;
        }
        int leftMax=arr[0];
        int rightMax=arr[arr.length-1];
        int l=1;
        int r=arr.length-2;
        long sum=0;
        while(l<=r){
            if(leftMax<rightMax){
                if(leftMax-arr[l]>0){
                    sum+=leftMax-arr[l];
                }
                 
                leftMax=Math.max(leftMax,arr[l]);
                l++;
            }else {
                if(rightMax-arr[r]>0){
                    sum+=rightMax-arr[r];
                }
                rightMax=Math.max(rightMax,arr[r]);
                r--;
            }
        }
        return sum;
    }

你可能感兴趣的:(数据结构,算法)