LeetCode:滑动窗口、双指针、单调栈

无重复字符的最长字串

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

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length()==0)
            return 0;
        HashMap<Character,Integer> map = new HashMap<>();
        int left = 0;
        int res = 0;
        for(int i =0; i<s.length();i++){
            char ch = s.charAt(i);
            if(map.containsKey(ch)){
                //更新窗口左边
                left = Math.max(left,map.get(ch)+1);
            }          
            map.put(ch,i);
            res = Math.max(res,i-left+1);            
        }
        return res;
    }
}

接雨水

42:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

双指针:

class Solution {
    public int trap(int[] height) {
        int left = 0;
        int right = height.length-1;
        int leftmax = height[left];//初始化
        int rightmax = height[right];
        int ans = 0;
        while(left<=right){
            if(leftmax<=rightmax){//如果左边最高 小于右边最高 那么右边有人兜底,可以从左往右把这次雨水算进去,否则会漏
                leftmax = Math.max(height[left],leftmax);//更新左边最高
                ans += leftmax - height[left];
                left++;
            }else{//如果左边最高 大于右边最高 那么左边有人兜底,可以从右往左把这次雨水算进去,否则会漏
                rightmax = Math.max(height[right],rightmax);//更新右边最高
                ans += rightmax - height[right];
                right--;
            }
        }
        return ans;
        
    }
}

单调栈:

class Solution {
    public int trap(int[] height) {
        if(height ==null || height.length<=2)
            return 0;
        LinkedList<Integer> stack = new LinkedList<>();
        int ans = 0;
        for(int right =0; right<height.length;right++){
            while(!stack.isEmpty()&&height[stack.peek()]<height[right]){
                //找到一个低洼处 并计算
                int bottom = stack.pop();
                if(stack.isEmpty())
                    break;
                int left = stack.peek();
                int leftheight = height[left];
                int bottomheight = height[bottom];
                int rightheight = height[right];
                //由于计算完低洼处,低洼处的index就已经被弹出了,所以right-left是有跨度的
                ans += (Math.min(leftheight,rightheight)-bottomheight)*(right-left-1);
            }
            stack.push(right);
        }
        return ans;
    }
}

盛水最多的容器

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

class Solution {
    public int maxArea(int[] height) {
        int result = 0;
        int left = 0;
        int right = height.length-1;
        while(left<right){
            result =Math.max(result,Math.min(height[left],height[right])*(right-left));
            //移动短的那边,水的容量才可能变大,移动长的不仅底部宽度变小,两边高的较小者也不会变大
            if(height[left]<height[right])
                left++;
            else
                right--;
        }
        return result;
    }
}

滑动窗口的最大值

239:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

利用优先队列,找到当前处理窗口之内的最大值

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {

        int count = nums.length-k +1;
        int[] result = new int[count];
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o2[0]-o1[0];
            }
        });
        for(int i=0;i<k;i++){
            queue.offer(new int[]{nums[i],i});
        }
        result[0] = queue.peek()[0];
        for(int i=k;i<nums.length;i++){
            queue.offer(new int[]{nums[i],i});
            while(queue.peek()[1]<i-k+1){
                queue.poll();
            }
            result[i-k+1] = queue.peek()[0];
        }
        return result;

    }
}

单调队列 

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        LinkedList<Integer> queue = new LinkedList<>();
        int[] ans = new int[nums.length-k+1]; 
        for(int i=0;i<k;i++){
            while(!queue.isEmpty()&&nums[i]>=nums[queue.peekLast()]){
                queue.pollLast();
            }
            queue.offerLast(i);
        }
        ans[0] = nums[queue.peekFirst()];
        for(int i = k;i<nums.length;i++){
            while(!queue.isEmpty()&&nums[i]>=nums[queue.peekLast()]){
                queue.pollLast();
            }
            queue.offerLast(i);
            while(queue.peekFirst()<i-k+1){
                queue.pollFirst();
            }
            ans[i-k+1] = nums[queue.peekFirst()];
        }
        return ans;
    }
}

每日温度

739:请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

单调递减栈:

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] result = new int[temperatures.length];
        LinkedList<Integer> stack = new LinkedList<>();
        for(int i=0;i<temperatures.length;i++){
            while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
                int pre = stack.pop();
                result[pre] = i-pre;
            }
            stack.push(i);
        }
        return result;
        
    }
}

长度最小的子数组

209:给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int left =0;
        int cursum =0 ;
        int right =0;
        int minlength =Integer.MAX_VALUE;
        while(right<nums.length){
            cursum+=nums[right];
            //如果找到比target大的和,尝试缩小窗口,直到小于为止
            while(cursum>=target&&left<=right){
                minlength = Math.min(right-left+1,minlength);
                cursum -=nums[left];
                left++;
            }
            right++;
        }
        if(left==0&&cursum<target)
            return 0;
        return minlength;

    }
}

移掉K位数字

402:给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

class Solution {
    public String removeKdigits(String num, int k) {
        StringBuilder res = new StringBuilder();
        int count = num.length()-k;
        LinkedList<Character> stack = new LinkedList<>();
        char[] chars = num.toCharArray();
        for(char ch : chars){
            while(k>0&&!stack.isEmpty()&&ch<stack.peek()){
                stack.pop();
                k--;
            }
            //开头是0
            if(stack.size()==0&&ch=='0'){
                count--;
                continue;
            }
                
            stack.push(ch);
        }
        System.out.println(stack);
        while(!stack.isEmpty()&&count>0){
            
            res.append(stack.pollLast());
            count--;
        }
        return res.toString().equals("")?"0":res.toString();
    }
}

最小覆盖字串

76:给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

class Solution {
    public String minWindow(String s, String t) {
        if(s == null||s.length()==0||t==null||t.length()==0){
            return "";
        }
        int[] need =new int[128];
        //正数代表还需要,负数和0代表以及满足数量需求
        for(int i=0;i<t.length();i++){
            need[t.charAt(i)]++;
        }
        int left = 0,right=0,count=t.length(),size = Integer.MAX_VALUE,start=0;
        while(right<s.length()){
            char ch = s.charAt(right);
            //如果是t中还需要的元素,count减1
            if(need[ch]>0){
                count--;
            }
            need[ch]--;
            //包含t的所有元素
            if(count==0){
                //尝试缩小左边界,将多余的元素去除,对应的need需求+1,
                while(left<right&&need[s.charAt(left)]<0){
                    need[s.charAt(left)]++;
                    left++;
                }
                //能缩小的最终状态,查看窗口大小,最左端肯定是t中元素,因为need[s.charAt(left)]==0,往右移动肯定需要+1
                if(right-left+1<size){
                    size = right-left+1;
                    start = left;
                }
                //左端向右移动,对应需求量+1
                need[s.charAt(left)]++;
                left++;
                count++;
            }
            right++;
        }
        return size == Integer.MAX_VALUE?"":s.substring(start,start+size);

    }
}

得到连续 K 个 1 的最少相邻交换次数

1703:前缀和+滑动窗口 

给你一个整数数组 nums 和一个整数 k 。 nums 仅包含 0 和 1 。每一次移动,你可以选择 相邻 两个数字并将它们交换。请你返回使 nums 中包含 k 个 连续 1 的 最少 交换次数。

class Solution {
    public int minMoves(int[] nums, int k) {
        int n = nums.length;
        int ans = Integer.MAX_VALUE;
        List<Integer> list = new ArrayList<>();
        // 1. 找到每个1的位置,并将其nums[i]-i的存入,i<-[0,1的总数],方便后续前缀和计算
        for(int i=0;i<n;i++){
            if(nums[i]==1)
                list.add(i-list.size());
        }
        // sum[i] --- 前i个的和 ai-i 
        int[] sum = new int[list.size()+1];
        for(int i=1;i <= list.size();i++){
            sum[i] = sum[i-1] + list.get(i-1);
        }
        // 滑动大小为 k 的窗口,计算每个窗口的最优值
        for(int i=k;i<=list.size();i++){ // i最好从k开始,代表在sum中的下标
            int left = i-k+1; 
            int right = i;
            int mid = (left+right)/2;
            int x = list.get(mid-1);
            int leftSum = (mid-left)*x-(sum[mid-1]-sum[left-1]);
            int rightSum = sum[right]-sum[mid] -(right-mid)*x;
            ans = Math.min(ans,leftSum+rightSum);
        }
        return ans;
    }
}

有效三角形个数

611:给定一个包含非负整数的数组 nums,返回其中可以组成三角形三条边的三元组个数。

class Solution {
    public int triangleNumber(int[] nums) {
        Arrays.sort(nums);
        int length = nums.length;
        int left = 0, right = 0;
        int res = 0;
        for(int i = length-1;i >= 2; i--){
            left = 0;
            right = i-1;
            while(left<right){
                if(nums[left]+nums[right]>nums[i]){
                    res += right - left;
                    right--;
                }else{
                    left++;
                }
            }
        }
        return res;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值