力扣LeetCode-数组

数组

一、二分查找

应用条件

  • 查找空间有序

算法描述

利用l,r两个指针分别指向查找空间首尾,比较中间值与目标值,移动l,r两个指针逐渐逼近目标值

模板1(常用)

用于查找目标值下标

int binarySearch(vector& nums, int target){
  if(nums.size() == 0)
    return -1;
​
  int left = 0, right = nums.size() - 1;
  while(left <= right){
    // 防止溢出
    int mid = left + (right - left) / 2;
    if(nums[mid] == target){ return mid; }
    else if(nums[mid] < target) { left = mid + 1; }
    else { right = mid - 1; }
  }
​
  // 结束条件:left==right
  return -1;
}

模板2

用于寻找目标值左侧边界

由于right=空间长度,所以循环条件为left,搜索空间为[left,right),最终跳出循环条件为left==right,每次循环时有right对应值大于等于mid对应值,可认为right作用为取得目标值左边界,left每次执行加1操作,可认为缩小搜索空间范围,调整mid值,跳出循环时right==left返回哪一个都一样,而未找到时,返回-1。

int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; // 注意
    
    while (left < right) { // 注意
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid; // 注意
        }
    }
    // target 比所有数都大
    if (left == nums.length) return -1;
    // 类似之前算法的处理方式
    return nums[left] == target ? left : -1;
}

模板3

用于寻找目标值右侧边界

同上跳出循环时left==right,此时right对应值始终大于目标值,用于标定目标值右侧一位,left用于缩小查找空间,直至left==right跳出循环,当未找到时返回-1。

int right_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0, right = nums.length; // 注意
    
    while (left < right) { // 注意
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            left = mid + 1; // 注意
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    if (left == 0) return -1;
    return nums[left-1] == target ? (left-1) : -1; //  注意 返回 left-1
}

练习题

  • LeetCode 34

  • LeetCode 35

二、双指针

算法描述

通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

注意事项

  • 分清快慢指针作用

  • 边界条件,防止越界访问

例题

移除nums数组中等于val的元素,剩余元素相对顺序不变。

代码

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

练习题

  • LeetCode 26.删除排序数组中的重复项

  • LeetCode 283.移动零

三、滑动窗口

算法描述

滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出满足要求的子序列。

主要思想:计算序列右边界扩展新元素对目标值产生的影响,序列左边界丢弃元素对目标值产生影响。

而实际上对于每一个数组元素,仅有入子序列与出子序列两种操作,所以为O(n)算法。

当右边界扩展生成的子序列满足要求时,记录或比较其与历史最优值并进行更新,左边界丢弃一个元素,右边界继续扩展直至满足条件再次判断。

前提条件

在右边界扩展生成一个满足条件子序列后,此时右边界继续右移对于目标求解没有贡献,而需移动左边界继续下一次寻解。

例题

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

代码

class Solution {
public:
    int minSubArrayLen(int s, vector& nums) {
        int result = INT32_MAX;
        int sum = 0; // 滑动窗口数值之和
        int i = 0; // 滑动窗口起始位置
        int subLength = 0; // 滑动窗口的长度
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while (sum >= s) {
                subLength = (j - i + 1); // 取子序列的长度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

练习题

  • Leetcode 904 水果成篮

  • LeetCode 76 最小覆盖子串

最小覆盖子串

题目

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

注意

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

代码

class Solution {
public:
    string minWindow(string s, string t) {
        vectorarray(128, 0); // 用于记录小字符串中每个字母出现次数
        int start=-1, l=0, r=0, count=t.size(), size=INT_MAX;  // count用于判断每次生成新的子串是否符合要求
        for(int i=0; i 0)
                count--;
            array[s[r]]--;  // 对于未在小字符串中出现的字符,为记录其在l至r子字符串中出现次数的相反数
            if(count == 0){
                while(l 
  

变量意义

array数组用于记录lr子串为满足条件其中元素需要出现个数。

r用于每次向右扩展元素,直至满足条件。

l移动用于除去不需要的字符,或打破满足条件进行下一次寻解。

count用于判断lr子串何时满足条件。

startsize用于记录最优解。

逻辑过程

1、用array数组记录子串需出现各元素个数。

2、利用r扩展元素至满足count(子串所需元素)为0。

3、左边界l丢弃多余元素。

4、更新最优解startsize

5、移动l打破count==0条件,进行下一次寻解。

你可能感兴趣的:(LeetCode,leetcode,算法,数组)