leetcode(数组专题)

1.二分查找

1.1 导论

  • 注意区间:

左闭右闭区间:[left,right] —>即可以取到left == right 所以循环写成while(left<=right).
左闭右开区间:[left,right) —>即不可以取到left == right 所以循环写成while(left<right).
其实只要把握了能不能取到的问题,写代码时就不会乱了。

  • 注意mid取值:

为了防止溢出要写成mid = left + (right-left)/2 或则 mid = left + ((right - left) >> 1);

  • 注意边界:

边界的取值完全是由区间来决定的,把握好区间也就把握好了边界取值

1.2 704.二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。

class Solution {
    public int search(int[] nums, int target) {
        int l = 0;
        int r = nums.length-1;
        while(l<=r){//左闭右闭
            int mid = l+(r-l)/2;
            if(target == nums[mid]){
                return mid;
            }else if(target<nums[mid]){
                r = mid-1;
            }else{
                l =mid+1;
            }
        }
        return -1;
    }
}
class Solution {
    public int search(int[] nums, int target) {
        int l = 0;
        int r = nums.length;
        while(l<r){//左闭右开
            int mid = l+(r-l)/2;
            if(target == nums[mid]){
                return mid;
            }else if(target<nums[mid]){
                r = mid;
            }else{
                l =mid+1;
            }
        }
        return -1;
    }
}

1.3 35.搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 为无重复元素的升序排列数组
-104 <= target <= 104

class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length;
        int left = 0, right = n - 1, ans = n;
        while (left <= right) {//左闭右闭
            int mid = ((right - left) >> 1) + left;
            if (target <= nums[mid]) {
                ans = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
}

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length-1;
        while(left<=right){//左闭右闭
            int mid = left+(right-left)/2;
            if(target<=nums[mid]){//找第一个满足条件的
                right = mid-1;
            }else{
                left = mid+1;
            }
        }
        return left;
    }
}
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        int ans = nums.length;
        while(left<right){//左闭右开
            int mid = left+(right-left)/2;
            if(target<=nums[mid]){
                right = mid;
                ans = mid;
            }else{
                left = mid+1;
            }
        }
        return ans;
    }
}

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        while(left<right){//左闭右开
            int mid = left+(right-left)/2;
            if(target<=nums[mid]){
                right = mid;
            }else{
                left = mid+1;
            }
        }
        return right;
    }
}

1.4 34.在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:

你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftIdx = binarySearch(nums, target, true);
        int rightIdx = binarySearch(nums, target, false) - 1;
        if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
            return new int[]{leftIdx, rightIdx};
        } 
        return new int[]{-1, -1};
    }

    public int binarySearch(int[] nums, int target, boolean lower) {
        int left = 0, right = nums.length - 1, ans = nums.length;
        while (left <= right) {//左闭右闭
            int mid = (left + right) / 2;
            if (nums[mid] > target || (lower && nums[mid] >= target)) {
                right = mid - 1;
                ans = mid;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
}


class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftIdx = binarySearch(nums, target, true);
        int rightIdx = binarySearch(nums, target, false) - 1;
        if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
            return new int[]{leftIdx, rightIdx};
        } 
        return new int[]{-1, -1};
    }

    public int binarySearch(int[] nums, int target, boolean lower) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {//左闭右闭
            int mid = (left + right) / 2;
            if (nums[mid] > target || (lower && nums[mid] >= target)) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftIdx = binarySearch(nums, target, true);
        int rightIdx = binarySearch(nums, target, false) - 1;
        if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
            return new int[]{leftIdx, rightIdx};
        } 
        return new int[]{-1, -1};
    }

    public int binarySearch(int[] nums, int target, boolean lower) {
        int left = 0, right = nums.length,ans = nums.length;
        while (left < right) {//左闭右开
            int mid = (left + right) / 2;
            if (nums[mid] > target || (lower && nums[mid] >= target)) {
                right = mid;
                ans = mid;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
}


class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftIdx = binarySearch(nums, target, true);
        int rightIdx = binarySearch(nums, target, false) - 1;
        if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
            return new int[]{leftIdx, rightIdx};
        } 
        return new int[]{-1, -1};
    }

    public int binarySearch(int[] nums, int target, boolean lower) {
        int left = 0, right = nums.length;
        while (left < right) {//左闭右开
            int mid = (left + right) / 2;
            if (nums[mid] > target || (lower && nums[mid] >= target)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return right;
    }
}

1.5 69.x的平方根

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

0 <= x <= 231 - 1

class Solution {
    public int mySqrt(int x) {
        int l=0;
        int r=x;
        int ans = x;
        while(l<=r){
            int mid = l+(r-l)/2;
            if((long)mid*mid <= x){//找最后一个满足条件的
                ans = mid;
                l= mid+1;
            }else{
                r=mid-1;
            }
        }
        return ans;
    }
}

class Solution {
    public int mySqrt(int x) {
        int l=0;
        int r=x;
        while(l<=r){
            int mid = l+(r-l)/2;
            if((long)mid*mid <= x){
                l= mid+1;
            }else{
                r=mid-1;
            }
        }
        return r;
    }
}

1.6 367.有效的完全平方数

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

进阶:不要 使用任何内置的库函数,如 sqrt 。

1 <= num <= 2^31 - 1

class Solution {
    public boolean isPerfectSquare(int num) {
        long l = 0;
        long r = num;
        while(l<=r){
            long mid = l+(r-l)/2;
            if(mid*mid == num){
                return true;
            }else if(mid*mid > num){
                r = mid - 1;
            }else{
                l = mid + 1;
            }
        }
        return false;
    }
}
class Solution {
    public boolean isPerfectSquare(int num) {
        long l = 0;
        long r = num+1;
        while(l<r){
            long mid = l+(r-l)/2;
            if(mid*mid == num){
                return true;
            }else if(mid*mid > num){
                r = mid;
            }else{
                l = mid + 1;
            }
        }
        return false;
    }
}

2.双指针

2.1 27.移除元素

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

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

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

0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100

class Solution {
    public int removeElement(int[] nums, int val) {
        int l = 0;
        for(int r =0;r<nums.length;++r){
            if(nums[r] != val){
                nums[l++] = nums[r];
            }
        }
        return l;
    }
}

class Solution {
    public int removeElement(int[] nums, int val) {
        int l = 0;
        int r = 0;
        while(r<nums.length){
            if(nums[r] != val){
                nums[l++] = nums[r];
            }
            r++;
        }
        return l;
    }
}

2.2 26.删除有序数组中的重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

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

0 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums 已按升序排列

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length == 0){
            return 0;
        }
        int left= 1;
        int right = 1;
        while(right<nums.length){
            if(nums[right]!=nums[right-1]){
                nums[left++]=nums[right];
            }
            right++;
        }
        return left;
    }
}

2.3 283.移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。

class Solution {
    public void moveZeroes(int[] nums) {
        int n = nums.length, left = 0, right = 0;
        while (right < n) {
            if (nums[right] != 0) {
                swap(nums, left, right);
                left++;
            }
            right++;
        }
    }

    public void swap(int[] nums, int left, int right) {
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

2.4 844.比较含退格的字符串

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。# 代表退格字符。

如果相等,返回 true ;否则,返回 false 。

注意:如果对空文本输入退格字符,文本继续为空。

1 <= s.length, t.length <= 200
s 和 t 只含有小写字母以及字符 ‘#’

class Solution {
    public boolean backspaceCompare(String S, String T) {
        int i = S.length() - 1, j = T.length() - 1;
        int skipS = 0, skipT = 0;

        while (i >= 0 || j >= 0) {
            while (i >= 0) {
                if (S.charAt(i) == '#') {
                    skipS++;
                    i--;
                } else if (skipS > 0) {
                    skipS--;
                    i--;
                } else {
                    break;
                }
            }
            while (j >= 0) {
                if (T.charAt(j) == '#') {
                    skipT++;
                    j--;
                } else if (skipT > 0) {
                    skipT--;
                    j--;
                } else {
                    break;
                }
            }
            if (i >= 0 && j >= 0) {
                if (S.charAt(i) != T.charAt(j)) {
                    return false;
                }
            } else {
                if (i >= 0 || j >= 0) {
                    return false;
                }
            }
            i--;
            j--;
        }
        return true;
    }
}


2.5 977.有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 已按 非递减顺序 排序

class Solution {
    public int[] sortedSquares(int[] nums) {
        int left = 0;
        int right = nums.length-1;
        int[] ans = new int[nums.length];
        int index = nums.length-1;
        while(left<=right){
            if(nums[left]*nums[left] <= nums[right]*nums[right]){
                ans[index--] = nums[right]*nums[right];
                --right;
            }else{
                ans[index--] = nums[left]*nums[left];
                ++left;
            }
        }
        return ans;
    }
}

2.6 15.三数之和

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

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

0 <= nums.length <= 3000
-105 <= nums[i] <= 105

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    List<Integer> list = new ArrayList<Integer>();
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    ans.add(list);
                }
            }
        }
        return ans;
    }
}

2.7 18.四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案

1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> quadruplets = new ArrayList<List<Integer>>();
        if (nums == null || nums.length < 4) {
            return quadruplets;
        }
        Arrays.sort(nums);
        int length = nums.length;
        for (int i = 0; i < length - 3; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {//去重
                continue;
            }
            if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
                break;
            }
            if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
                continue;
            }
            for (int j = i + 1; j < length - 2; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1]) {//去重
                    continue;
                }
                if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
                    break;
                }
                if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
                    continue;
                }
                int left = j + 1, right = length - 1;
                while (left < right) {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum == target) {
                        quadruplets.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        while (left < right && nums[left] == nums[left + 1]) {//去重
                            left++;
                        }
                        left++;
                        while (left < right && nums[right] == nums[right - 1]) {//去重
                            right--;
                        }
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        right--;
                    }
                }
            }
        }
        return quadruplets;
    }
}

3.滑动窗口

3.1导论

  • 滑动窗口就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
  • 核心:

窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?

  • 其实也是属于双指针的一种,只不过是两个指针维护一个窗口。

3.2 209.长度最小子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105

class Solution {
    private int count = 0;
    public int minSubArrayLen(int target, int[] nums) {
    	int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int result = Integer.MAX_VALUE;
        int sum = 0;
        int left=0;
        int windowLength = 0;
        for(int right=0;right<n;++right){
            sum += nums[right];
            while(sum >= target){
                windowLength = right-left+1;
                result = result <= windowLength ? result : windowLength;
                sum -= nums[left++]; 
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int ans = Integer.MAX_VALUE;
        int start = 0, end = 0;
        int sum = 0;
        while (end < n) {
            sum += nums[end];
            while (sum >= target) {
                ans = Math.min(ans, end - start + 1);
                sum -= nums[start++];
            }
            end++;
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}

3.3 904.水果成蓝

在一排树中,第 i 棵树产生 tree[i] 型的水果。
你可以从你选择的任何树开始,然后重复执行以下步骤:

把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
移动到当前树右侧的下一棵树。如果右边没有树,就停下来。
请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1,然后执行步骤 2,依此类推,直至停止。

你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。

用这个程序你能收集的水果树的最大总量是多少?

1 <= tree.length <= 40000
0 <= tree[i] < tree.length


// 这道题可以理解为求只包含两种元素的最长连续子序列
class Solution {
    public int totalFruit(int[] tree) {
        int ans = 0, i = 0;
        Counter count = new Counter();
        for (int j = 0; j < tree.length; ++j) {
            count.add(tree[j], 1);
            while (count.size() >= 3) {
                count.add(tree[i], -1);
                if (count.get(tree[i]) == 0)
                    count.remove(tree[i]);
                i++;
            }

            ans = Math.max(ans, j - i + 1);
        }

        return ans;
    }
}

class Counter extends HashMap<Integer, Integer> {
    public int get(int k) {
        return containsKey(k) ? super.get(k) : 0;
    }

    public void add(int k, int v) {
        put(k, get(k) + v);
    }
}


class Solution {
    public int totalFruit(int[] tree) {
        int ans = 0, i = 0;
        Map<Integer,Integer> map = new HashMap<>();
        for (int j = 0; j < tree.length; ++j) {
            map.put(tree[j],map.getOrDefault(tree[j],0) + 1);
            while (map.size() >= 3) {
                if (map.containsKey(tree[i])) {
                    map.put(tree[i],map.getOrDefault(tree[i],0) - 1);
                    if (map.get(tree[i]) == 0)
                        map.remove(tree[i]);
                }
                
                i++;
            }

            ans = Math.max(ans, j - i + 1);
        }

        return ans;
    }
}

3.4 76.最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

1 <= s.length, t.length <= 105
s 和 t 由英文字母组成

class Solution {
    public String minWindow(String s, String t) {
        int sLength = s.length();
        int tLength = t.length();
        if(sLength < tLength){
            return "";
        }

        HashMap<Character,Integer> tMap = new HashMap<>();//记录t字符串各个字符的数量
        for(int i=0;i<tLength;++i){
            tMap.put(t.charAt(i),tMap.getOrDefault(t.charAt(i),0)+1);
        }

        int left = 0;//滑动窗口起始位置
        int sublength = Integer.MAX_VALUE;//记录子串长度
        int valid = 0;//用来记录滑动窗口中有多少字符是与t字符串中的各个字符数量相等
        int start = 0;//记录最小子串起始位置
        HashMap<Character,Integer> sMap = new HashMap<>();//用来记录滑动窗口中各个字符的数量
        
        for(int right = 0;right<sLength;++right){
            char addChar = s.charAt(right);
            sMap.put(addChar,sMap.getOrDefault(addChar,0)+1);
            //注意:这个地方比较要使用equals 因为是Integer包装类,如果用==比较的是地址
            if(tMap.containsKey(addChar) && sMap.get(addChar).equals(tMap.get(addChar))){
                valid++;
            }
            while(valid == tMap.size()){//若相等,说明滑动窗口中已经覆盖了字符串t中的所有字符
                if(right-left+1 < sublength){
                    sublength = right-left+1;
                    start = left;
                }
                char removeChar = s.charAt(left++);//滑动窗口起始位置滑动
                if(tMap.containsKey(removeChar) && sMap.get(removeChar).equals(tMap.get(removeChar))){
                    valid--;
                }
                sMap.put(removeChar,sMap.get(removeChar)-1);
            }
        }
        return sublength == Integer.MAX_VALUE ? "" : s.substring(start,start+sublength);
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Listen·Rain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值