遍历hashset for_双指针遍历/滑动窗口

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

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

示例 1:

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

示例 2:

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

示例 3:

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

自己的做法:遍历每个字符作为起点,去寻找无重复字符的子串,时间复杂度

使用HashSet,空间复杂度

表示字符集
import java.util.HashSet;

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.equals("")) return 0;
        HashSet<Character> hashset = new HashSet<>();
        hashset.add(s.charAt(0));
        int p = 0;
        int q = 1;
        int max = -1;
        int len = 1;
        while (true) {
            if (q == s.length()){
                if (max < len)
                    max = len;
                break;
            }
            if (hashset.contains(s.charAt(q))) {
                if (max < len)
                    max = len;
                len = 1;
                p = p + 1;
                q = p + 1;
                hashset.clear();
                hashset.add(s.charAt(p));
            } else {
                len++;
                hashset.add(s.charAt(q));
                q = q + 1;
            }
        }
        return max;
    }
}

官方题解:

发现了什么?如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的!这里的原因在于,假设我们选择字符串中的第k个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为
。那么当我们选择第 k+1个字符作为起始位置时,首先从 k+1到
的字符显然是不重复的,并且由于少了原本的第k个字符,我们可以尝试继续增大
,直到右侧出现了重复字符为止。

滑动窗口,维护在窗口内没有重复字符

import java.util.HashSet;

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashSet<Character> hashSet = new HashSet<>();
        int rk = -1;
        int max = 0;
        int len = 0;
        for (int i = 0; i < s.length(); i++) {
            if (i != 0)
                hashSet.remove(s.charAt(i - 1));
            while (rk + 1 < s.length() && !hashSet.contains(s.charAt(rk + 1))) {
                rk++;
                hashSet.add(s.charAt(rk));
            }
            if (max < hashSet.size())
                max = hashSet.size();
        }
        return max;
    }
}

11. 盛最多水的容器

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

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

58c2f6a2ecf30e0522b6b4d13b22cc36.png

图中垂直线代表输入数组 [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 p = 0;
        int q = height.length - 1;
        int max = 0;
        while (p != q) {
            if (max < Math.min(height[p], height[q]) * (q - p))
                max = Math.min(height[p], height[q]) * (q - p);
            if (height[p] < height[q])
                p++;
            else
                q--;
        }
        return max;
    }
}

基本思路

对于数组height第p个元素到第q个元素,寻找最大面积。(不妨设height[p]

height[q])

也就是说p指针向右移动,才有可能最大面积

问题转换为:对于数组height第p+1个元素到第q个元素,寻找最大面积。

扫描一遍数组,时间复杂度

空间复杂度

证明

移动q指针不会使得面积更大

假设q向左移动到

如果

如果

如果

也就是说移动height较大的指针,不会使得面积变大

只能移动height较小的指针


15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

自己写的bug频出:-(

看了题解,思路如下:

有a b c三个指针,a遍历一遍数组

观察到当

时,
,有

也就是说a一定的时候,b向右移动,c向左移动

a遍历一遍,n个元素,b,c在a之后剩余的元素里面相向而行,总共走了n个元素

时间复杂度

排序需要

的空间复杂度
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<>();
        for (int first = 0; first < nums.length; first++) {
            int third = nums.length - 1;
            if (first != 0 && nums[first] == nums[first - 1])
                continue;
            for (int second = first + 1; second < third; second++) {
                if (second > first + 1 && nums[second] == nums[second - 1])
                    continue;
                while (second<third && nums[first] + nums[second] + nums[third] > 0)
                    third--;
                if(second == third) break;
                if (nums[first] + nums[second] + nums[third] == 0) {
                    List<Integer> list = new ArrayList<Integer>();
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    ans.add(list);
                }
            }
        }
        return ans;
    }
}

16. 最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

基本思路

数组排序

三个指针

,
从头开始遍历,
开始遍历,
从n-1开始遍历

向右移动,sum越来越大,所以只能向左移动

向左移动,sum越来越小,所以只能向右移动
class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int ans = 999999;
        for (int first = 0; first < nums.length; first++) {
            int second = first + 1;
            int third = nums.length - 1;
            while (second < third){
                int tmp = nums[first] + nums[second] + nums[third];
                if (Math.abs(tmp - target) < Math.abs(ans - target)){
                    ans = tmp;
                }
                if (tmp > target){
                    third--;
                }
                else if (tmp < target){
                    second++;
                }
                else{
                    return tmp;
                }
            }
        }
        return ans;
    }
}

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 {
    public int removeDuplicates(int[] nums) {
        int p = 1;
        int q = 1;
        while (q < nums.length){
            if (nums[q] == nums[q-1])
                q++;
            else{
                nums[p] = nums[q];
                p++;
                q++;
            }
        }
        return p;
    }
}

42. 接雨水

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

250fc2dbb5f47fd73f80f986cf8bd202.png

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例:

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

双指针

class Solution {
    public int trap(int[] height) {
        if (height.length == 0 || (height.length == 1 && height[0] == 0)) return 0;
        int hmax = height[0];
        int index = 0;
        for (int i = 1; i < height.length; i++) {
            if (hmax < height[i]) {
                hmax = height[i];
                index = i;
            }
        }
        int p = 0;
        while (height[p] == 0)
            p++;
        int q = p + 1;
        int water = 0;
        while (p < index) {
            if (height[q] < height[p]) {
                water += height[p] - height[q];
                q++;
            } else {
                p = q;
                q = p + 1;
            }
        }
        p = height.length - 1;
        while (height[p] == 0)
            p--;
        q = p - 1;
        while (p > index) {
            if (height[q] < height[p]) {
                water += height[p] - height[q];
                q--;
            } else {
                p = q;
                q = p - 1;
            }
        }
        return water;
    }
}

时间复杂度

空间复杂度

import java.util.Deque;
import java.util.LinkedList;

class Solution {
    public int trap(int[] height) {
        if (height.length == 0 || (height.length == 1 && height[0] == 0)) return 0;
        Deque<Integer> stack = new LinkedList<>();
        int current = 0;
        int ans = 0;
        while (current < height.length) {
            while (!stack.isEmpty() && height[current]>height[stack.peek()]){
                int top = stack.pop();
                if (stack.isEmpty())
                    break;
                int distance = current - stack.peek() - 1;
                int h = Math.min(height[stack.peek()], height[current]) - height[top];
                ans += distance*h;
            }
            stack.push(current);
            current++;
        }
        return ans;
    }
}

121. 买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

思路

62ca93a93fee478c280244bf09684572.png
class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length == 0) return 0;
        int minprice = prices[0];
        int ans = 0;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] < minprice)
                minprice = prices[i];
            if (ans < prices[i] - minprice)
                ans = prices[i] - minprice;
        }
        return ans;
    }
}

遍历一遍,时间复杂度

空间复杂度


209. 长度最小的子数组

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

示例:

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

滑动窗口

维护一个窗口,这个窗口内的和比s大,就向右移动右指针,加入新数,这个窗口内的和比s小,就向右移动左指针,移除旧数

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        if (nums.length == 0) return 0;
        int i = 0;
        int j = 0;
        int sum = nums[0];
        int min = 999999;
        while (i < nums.length && j < nums.length) {
            while (sum < s) {
                j++;
                if (j >= nums.length) break;
                sum += nums[j];
            }
            if (min > j - i + 1 && j < nums.length)
                min = j - i + 1;
            sum -= nums[i];
            i++;
        }
        if (min == 999999) return 0;
        return min;
    }
}

左指针遍历一遍,

,右指针遍历一遍,
,总的时间复杂度

空间复杂度

从题解看到了前缀和+二分查找的方法

前缀和

对于第i个元素,希望通过二分查找找到bound,使得

自己写的一直通不过,这里摘抄官方题解

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int ans = Integer.MAX_VALUE;
        int[] sums = new int[n + 1]; 
        // 为了方便计算,令 size = n + 1 
        // sums[0] = 0 意味着前 0 个元素的前缀和为 0
        // sums[1] = A[0] 前 1 个元素的前缀和为 A[0]
        // 以此类推
        for (int i = 1; i <= n; i++) {
            sums[i] = sums[i - 1] + nums[i - 1];
        }
        for (int i = 1; i <= n; i++) {
            int target = s + sums[i - 1];
            int bound = Arrays.binarySearch(sums, target);
            if (bound < 0) {
                bound = -bound - 1;
            }
            if (bound <= n) {
                ans = Math.min(ans, bound - (i - 1));
            }
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值