leetcode_数组相关问题

本文总结了LeetCode中使用双指针法解决的数组问题,包括删除排序数组中的重复项、移除元素、颜色分类、合并两个有序数组、找到数组中的第K个最大元素等。通过双指针技巧,可以有效地在原地修改数组并保持较低的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

283.零移动

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

 

思路一:保证非零元素的相对顺序,冒泡排序法,时间复杂度为O(n^2),空间复杂度为O(1)

class Solution {
    //保证非零元素的相对顺序,冒泡排序法,时间复杂度为O(n^2),空间复杂度为O(1)
    public void moveZeroes(int[] nums) {
        
        //优化的冒泡排序法,记录最后一次交换元素的位置
        int lastSwap = 0;
        for(int i=nums.length-1; i>0; i=lastSwap){
            lastSwap = 0;
            for(int j=0; j<i; j++){
                if(nums[j]==0 && nums[j+1]!=0){
                    int temp = nums[j];
                    nums[j] = nums[j+1];
                    nums[j+1] = temp;
                    lastSwap = j;
                }
            }
        }       
    }
}

思路二:双指针法 

class Solution {
    //双指针k,l   移动l,当l处的元素非零时,便与k处的元素交换位置,同时k加一,维护[0,k)为非零元素
    //时间复杂度为O(n),空间复杂度为O(1)
    public void moveZeroes(int[] nums) {
        int k=0, l=0;
        for(;l<nums.length; l++){
            if(nums[l]!=0){
                if(l!=k){    // 优化点 省略自身交换
                    int temp = nums[l];
                    nums[l] = nums[k];
                    nums[k]= temp;   
                }                
                k++;
            }
        }
    }
}

 

 

腾讯2017暑期实习生编程题2:

把一个字符串的大写字母放到字符串的后面,各个字符的相对位置不变,且不能申请额外的空间。 

输入描述:  输入数据有多组,每组包含一个字符串s,且保证:1<=s.length<=1000.

输出描述:  对于每组数据,输出移位后的字符串。

输入例子1:  AkleBiCeilD

输出例子1:  kleieilABCD

 

思路一:冒泡排序

import java.util.*;

public class Main{
    
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        while(in.hasNext()){
            String s = in.nextLine();
            System.out.println(getResult(s));            
        }
    }
    
    public static String getResult(String input){
        char[] str = input.toCharArray();
        int lastSwap = 0;
        for(int i=str.length-1; i>0; i=lastSwap){
            lastSwap = 0;
            for(int j=0;j<i;j++){
                char c = str[j];
                char d = str[j+1];
                    
                if((c>='A'&& c<='Z') && (d>='a'&& d<='z')){
                    char temp = str[j];
                    str[j] = str[j+1];
                    str[j+1] = temp;
                }
                
                lastSwap = j;
            }
        }
        
        StringBuilder sb = new StringBuilder();
        for(int i=0; i<str.length; i++){
            sb.append(str[i]);
        }
        return sb.toString();
    }
}

思路二:双指针法,用两个指针,一个表示当前处理元素,一个表示下一个待放置小写字母的位置,每次处理将中间的所有大写字母移动一遍以保持大写字母原有顺序

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        while(in.hasNextLine()){
            String str = in.nextLine();
            System.out.println(getResult(str));
        }
    }
    
    public static String getResult(String s){
        char[] stochars = s.toCharArray();
        int k=0, l=0;
        for(;l<stochars.length; l++){
            if(stochars[l]>'Z'){
                for(int p = k; p<l; p++){
                    char temp = stochars[l];
                    stochars[l]= stochars[p];
                    stochars[p] = temp;
                }
                k++;
            }
        }
        return new String(stochars);
    }
    
}

 

26.删除排序数组中的排序项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。
 

示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

 

思路:双指针法

class Solution {
    //[0,k]内为不重复的元素,l用于遍历
    public int removeDuplicates(int[] nums) {
        int k=0, l=0;
        for(; l<nums.length; l++){
            if(nums[l] != nums[k]){
                k++;
                nums[k]=nums[l];
            }
        }
        return k+1;
    }
}

 

27. 移除元素

给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

给定 nums = [3,2,2,3], val = 3,

函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,1,2,2,3,0,4,2], val = 2,

函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

注意这五个元素可为任意顺序。

你不需要考虑数组中超出新长度后面的元素。

思路:双指针法

class Solution {
    //[0,k)为非val的元素,l用于遍历数组
    public int removeElement(int[] nums, int val) {
        int k=0, l=0;
        for(; l<nums.length; l++){
            if(nums[l]!=val){
                nums[k] = nums[l];
                k++;
            }
        }
        return k;
    }
}

 

80. 删除排序数组中的重复项 II

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定 nums = [1,1,1,2,2,3],

函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,0,1,1,1,1,2,3,3],

函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。

你不需要考虑数组中超出新长度后面的元素。

思路:双指针法

class Solution {
    public int removeDuplicates(int[] nums) {
        //双指针,[0,k]为符合要求的数据
        int k=1,l=2;
        for(;l<nums.length;l++){
            if(nums[l]!=nums[k-1]){
                k++;
                nums[k] = nums[l];
            }
        }
        return k+1;
    }
}

 

75. 颜色分类(三路快排思想)

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

思路一:计数排序

class Solution {
    public void sortColors(int[] nums) {
        int[] count = new int[3];
        for(int i=0; i<nums.length; i++){
            if(nums[i]==0){
                count[0]++;
            }else if(nums[i]==1){
                count[1]++;
            }else{
                count[2]++;
            }
        }
        
        int index=0;
        for(int i=0; i<count[0]; i++){
            nums[index++]=0;
        }
        for(int i=0; i<count[1]; i++){
            nums[index++]=1;
        }
        for(int i=0; i<count[2]; i++){
            nums[index++]=2;
        }
    }
}

思路二:快速排序中partition过程中的三路快排思想 ,空间复杂度为O(1).

class Solution {
    public void sortColors(int[] nums) {
        //快速排序 partition过程中的三路快排思想
        //定义三个指针a,b,c
        //保证[0,a]的值为0,[a+1,b)的值为1,(b,c]的值为2
        int a=-1, b=0, c=nums.length;
        while(b<c){
            if(nums[b]==0){
                a++;
                int temp = nums[b];
                nums[b] = nums[a];
                nums[a] = temp;
                b++;
            }else if(nums[b] == 1){
                b++;
            }else{
                c--;
                int temp = nums[c];
                nums[c] = nums[b];
                nums[b] = temp;                
            }
        }
    }
}

 

88. 合并两个有序数组 (归并排序中的归并 )

给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。

说明:

初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]

思路:归并排序中归并的过程 

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        //归并排序中归并的过程
        int a=0, b=0;
        int[] arr = new int[m+n];
        
        for(int i=0; i<arr.length; i++){
            if(a<m && b<n){
                if(nums1[a]<=nums2[b]){
                    arr[i]=nums1[a++];
                }else{
                    arr[i]=nums2[b++];
                }
            }else if(a>=m){
                arr[i]=nums2[b++];
            }else{
                arr[i]=nums1[a++];
            }            
        }
        
        for(int i=0; i<arr.length; i++){
            nums1[i]=arr[i];
        }
    }
}

 

215. 数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

经典的topK问题:

思路一:进行排序后再选择(时间复杂度为O(n^2) 或 O(logn))

class Solution {
    public int findKthLargest(int[] nums, int k) {
        quickSort(nums, 0, nums.length-1);
        return nums[k-1];
    }
    
    private int positition(int[] arr, int l, int r){
        //随机选取元素进行positition操作
        int index = (int)(Math.random()*(r-l+1)+l);
        int temp = arr[index];
        arr[index] = arr[l];
        arr[l] = temp;
        int v = arr[l];
        
        //双路快排
        int i=l+1, j=r;
        while(true){
            while(i<=r && arr[i]>v){
                i++;
            }
            
            while(j>=l+1 && arr[j]<v){
                j--;
            }
            
            if(i>j){
                break;
            }
            
            int temp0 = arr[i];
            arr[i] = arr[j];
            arr[j] = temp0;
            
            i++;
            j--;
        }
        
        int temp1 = arr[l];
        arr[l] = arr[j];
        arr[j] = temp1;
        
        return j;
    }
    
    private void quickSort(int[] arr, int l, int r){
        if(l>=r){
            return;
        }
        
        int p = positition(arr, l, r);
        quickSort(arr, l, p-1);
        quickSort(arr, p+1, r);
    }
}

思路二:使用优先队列(堆)

思路三:快速排序中partition过程的合理使用(使得时间复杂度为O(n) )

class Solution {
    public int findKthLargest(int[] nums, int k) {
       
        return findKth(nums, 0, nums.length-1, k-1);            
    }
    
    private int positition(int[] arr, int l, int r){
        //随机选取标定点
        int index = (int)(Math.random()*(r-l+1)+l);
        int temp = arr[index];
        arr[index] = arr[l];
        arr[l] = temp;
        int v = arr[l];
        
        //双路快排
        int i=l+1, j=r;
        while (true){                      //while判断为真, 可以保证即使只有两个元素时,即i=l+1=j 时,也可以进入循环
 
            while (i<=r && arr[i]>v){     //i停下的位置一定是小于或等于v的位置
                i++;
            }
            while (j>=l+1 && arr[j]<v){   //j停下的位置一定是大于或等于v的位置
                j--;
            }
 
            if(i>j){      //只能取大于
                break;
            }
            
            int temp0 = arr[i];
            arr[i]=arr[j];
            arr[j] = temp0;
            
            i++;
            j--;
        }
        int temp1 = arr[l];
        arr[l] = arr[j];
        arr[j]= temp1;     //j落在最后一个大于等于v的位置, i落在第一个小于等于v的位置,最终i不等于j, 而l处需要一个大于v的值,所以只能和 j 处的交换
        return j;   
    }
    
    private int findKth(int[] arr, int l, int r, int k){
        if(l==r){
            return arr[l];
        }
        
        int p = positition(arr, l, r);
        if(k==p){
            return arr[p];
        }else if(k<p){
           return findKth(arr, l, p-1, k);
        }else{
           return findKth(arr, p+1, r, k);
        }
    }
}

 

双索引中的对撞指针

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

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

思路一:暴力求解 ,时间复杂度为O(n^2)

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];
        tag: for(int i=0; i<numbers.length; i++){
            for(int j=i+1; j<numbers.length; j++){
                if(numbers[j]+numbers[i] == target){
                    res[0]=i+1;
                    res[1]=j+1;
                    break tag;
                }
            }
        }
        return res;
    }      
}

思路二:利用数组的有序性,使用二分查找法降低时间复杂度

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];
        tag:for(int i=0; i<numbers.length; i++){
            int t = binarySearch(numbers, i+1,numbers.length-1, target-numbers[i]);
            if(t!=-1){
                res[0] = i+1;
                res[1] = t+1;
                break tag;
            }
        }
        
        return res;      
    }
    
    //非递归实现二分搜索法
    private int binarySearch(int[] arr, int l, int r, int o){
        
        while(l<=r){
            int mid = (r-l)/2+l;
            if(arr[mid] == o){
                return mid;
            }else if(arr[mid] > o){
                r = mid-1;
            }else{
                l=mid+1;
            }          
        }
        return -1;
    }
}

思路三:充分利用数组的有序性,使用对撞指针,时间复杂度为O(n)

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];
        
        //对撞指针
        int k=0, l=numbers.length-1;
        while(numbers[k]+numbers[l]!=target){            
            if(numbers[k]+numbers[l]>target){
                l--;
            }else{
                k++;
            }                        
        }
        res[0] = k+1;
        res[1] = l+1;
        
        return res;      
    }
}

125. 验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:

输入: "race a car"
输出: false

思路:对撞指针

class Solution {
    public boolean isPalindrome(String s) {
        
        //预处理
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder();
        for(int i=0; i<s.length(); i++){
            char c = s.charAt(i);
            if((c>='0' && c<='9') || (c>='a' && c<='z')){
                sb.append(c);
            }
        }
        
        String ss = sb.toString();
        
        if(ss.length()<2){
            return true;
        }
        
        //对撞指针
        int i=0, j=ss.length()-1;
        while(i<=j){
            if(ss.charAt(i) != ss.charAt(j)){
                return false;
            }
            i++;
            j--;
        }
        
        return true;
    }
}

344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。


示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

思路:对撞指针

class Solution {
    public void reverseString(char[] s) {
        int i=0, j=s.length-1;
        while(i<=j){
            
            char temp = s[i];
            s[i] = s[j];
            s[j] = temp;
                       
            i++;
            j--;
        }
    }
}

345. 反转字符串中的元音字母

编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

示例 1:

输入: "hello"
输出: "holle"

示例 2:

输入: "leetcode"
输出: "leotcede"
说明:
元音字母不包含字母"y"。

思路:对撞指针

class Solution {
    public String reverseVowels(String s) {
        String o = "aeiouAEIOU";
        
        char[] cs = s.toCharArray();
        int k=0, l=s.length()-1;
        while(k<l){
            char m = cs[k];
            char n = cs[l];
            while(o.indexOf(m) ==-1 && k<s.length()-1){
                m = s.charAt(++k);
            }
            while(o.indexOf(n) ==-1 && l>0){
                n = s.charAt(--l);
            }
            
            if(k<l){   //防止k>l时,二次交换发生
                char temp = cs[k];
                cs[k] = cs[l];
                cs[l] = temp;
            }
                
            k++;
            l--;
        }
        
        return new String(cs);
    }
}

 

11. 盛最多水的容器

给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49

思路一:暴力求解

思路二:对撞指针法

class Solution {
    public int maxArea(int[] height) {        
        
        int l=0, r=height.length-1;
        
        int i=l, j=r;
        int maxV = (j-i)*Math.min(height[i],height[j]);
        
        while(i<j){
            
            if(height[i]<height[j]){           
                i++;                                
            }else{               
                j--;                                            
            }
                        
            if(i<j && (j-i)*Math.min(height[i],height[j])>maxV){
                maxV = (j-i)*Math.min(height[i],height[j]);                
            }  
        }
                   
        return maxV;        
    }
}

 

双指针中的滑动窗口

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。

示例: 

输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。

思路:双指针滑动窗口 

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        
        int l=0, r=nums.length-1;
        int length = r+2;   //数组的最长长度为r+1
        
        int i=l, j=l-1;    //滑动串口为[i,j], j<i时表示窗口中无元素,j=i时表示窗口中有一个元素
        int sum = 0;
        while(i<=r){
            if(j<r && sum < s){  //注意控制j的值,防止数组越界
                sum += nums[++j];
            }else{           
                sum -= nums[i++];
            }
            
            if(sum >= s && j-i+1<length){
                length = j-i+1;
            }
        }
        
        return length == r+2 ? 0 :length;     
    }       
}

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

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:
输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

 思路:双指针滑动窗口

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int length = 0;
        TreeMap<Character, Integer> map = new TreeMap<>(); //记录某个字符最后出现的位置
        
        char[] cs = s.toCharArray();
        int i=0, j=-1;
        while(j+1<cs.length){          
                j++;
                if(!map.containsKey(cs[j])){
                    map.put(cs[j], j);
                }else{
                    if(map.get(cs[j])>=i){
                        i=map.get(cs[j])+1;                        
                    }
                    map.put(cs[j], j);
                } 
                if(j-i+1>length){
                    length = j-i+1;
                }         
        }
        
        return length;
    }
}

 

438. 找到字符串中所有字母异位词

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。

说明:
字母异位词指字母相同,但排列不同的字符串。
不考虑答案输出的顺序。

示例 1:
输入:
s: "cbaebabacd" p: "abc"
输出:
[0, 6]

解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。

 示例 2:
输入:
s: "abab" p: "ab"
输出:
[0, 1, 2]

解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。

思路:双指针滑动窗口

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        ArrayList<Integer> list = new ArrayList<>();
        
        TreeMap<Character, Integer> pmap = new TreeMap<>();  //记录p中的元素
        TreeMap<Character, Integer> smap = new TreeMap<>();  //记录滑动窗口中的元素
        
        //统计p中的词频, 记录p中的内容
        for(char c : p.toCharArray()){
            if(!pmap.containsKey(c)){
                pmap.put(c, 1);
            }else{
                pmap.put(c, pmap.get(c)+1);
            }
        }
        
        int start=0, end=0;  //滑动窗口的两个边界 [start, end]
        int count=0;         //当滑动窗口中包含了p中的所有元素时,count=p.length()
        for(; end<s.length(); end++){
            //扩展右边界
            if(!smap.containsKey(s.charAt(end))){
                smap.put(s.charAt(end), 1);
            }else{
                smap.put(s.charAt(end), smap.get(s.charAt(end))+1);
            }
            
            if(pmap.containsKey(s.charAt(end)) && smap.get(s.charAt(end))<=pmap.get(s.charAt(end))){
                count++;
            }
            
            //滑动窗口中包含了p中的所有元素时,移动窗口左边界缩小窗口
            while(count == p.length()){
                if(end-start+1 == p.length()){
                    list.add(start);
                }
                
                //缩小左边界
                smap.put(s.charAt(start), smap.get(s.charAt(start))-1);
                if(pmap.containsKey(s.charAt(start)) && smap.get(s.charAt(start))<pmap.get(s.charAt(start))){
                    count--;
                }               
                start++;
            }
        }
        
        return list;        
    }
}

 

76. 最小覆盖子串

给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。

示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

说明:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

思路: 双指针滑动窗口

class Solution {
    public String minWindow(String s, String t) {
        int length = s.length()+1;
        String res = "";
        
        char[] cs = s.toCharArray();
        char[] ct = t.toCharArray();
        TreeMap<Character, Integer> tmap = new TreeMap<>();  //记录t中的内容
        TreeMap<Character, Integer> smap = new TreeMap<>();  //记录滑动窗口中的内容
        
        for(char c : ct){
            if(!tmap.containsKey(c)){
                tmap.put(c, 1);
            }else{
                tmap.put(c, tmap.get(c)+1);
            }
        }
        
        int count=0;  //当count=t.length()时,表示窗口中包含了t中的所有元素
        int start = 0, end = 0;  //窗口的两个边界 [start, end]
        for(; end<s.length(); end++){
            if(!smap.containsKey(cs[end])){
                smap.put(cs[end], 1);
            }else{
                smap.put(cs[end], smap.get(cs[end])+1);
            }
            
            if(tmap.containsKey(cs[end]) && smap.get(cs[end])<=tmap.get(cs[end])){
                count++;
            }
            
            //当窗口中包含了t中的所有元素时,需要缩小左边界
            while(count == t.length()){
                if(end-start+1<length){
                    length = end-start+1;
                    res = s.substring(start, end+1);
                }
                
                smap.put(cs[start], smap.get(cs[start])-1);
                if(tmap.containsKey(cs[start]) && smap.get(cs[start])<tmap.get(cs[start])){
                    count--;
                }
                
                start++;
            }                      
        }
        
        return res;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值