算法--数组 | 青训营笔记


theme: condensed-night-purple

highlight: a11y-dark

这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天

1. 数组

数组是存放在连续内存空间上的相同类型数据的集合。

数组可以方便的通过下标索引的方式获取到下标下对应的数据。

二分查找

数组为 有序数组,同时数组中无重复元素, 因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件

左闭右闭 [left, right]****

  • while (left <= right) **要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) *right 要赋值为 middle - 1*,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1

左闭右开 [left, right)**

  • while (left < right) ,这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

left + ((right -left) >> 1) == (left + right) /2

>>: 二进制右移

举个例子: 1010 >> 1 == 0101

1010 十进制 10

0101 十进制 5

综上 >> 1 作用相当于除二

所以 left + ((right -left) >> 1) ==> left + ((right -left)/2)

==> left + right/2 -left/2 ==> left/2 + right/2 ==> (left + right) /2

问题 :为什么不直接用(left + right) /2 而用left + ((right -left) >> 1)

答: 是因为left + right 在某种情况下可能会超过基本类型所能容纳的最大值,而且 >> (位运算) 比 / 运算要快一点

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

https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/submissions/

解题思路:

找左右边界,如果在数组中有一个target,在下标3,左右边界为【4,5】

找边界:target在左边记录right,会越往左走

target在右边记录left,会越往右走

在return时,

  • 只要有一个边界为-2, target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1} if(leftBorder === -2 || rightBorder === -2 )
  • target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}

if (rightBorder - leftBorder > 1)         return [leftBorder + 1, rightBorder - 1]

  • target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1} return [-1, -1]

移除元素

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

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

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

暴力解法

这个题目暴力的解法就是两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。

双指针法

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

定义快慢指针

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • 慢指针:指向更新 新数组下标的位置

注意这些实现方法并没有改变元素的相对位置

int slowIndex = 0; for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) { if (val != nums[fastIndex]) { nums[slowIndex++] = nums[fastIndex]; } } return slowIndex;

题目:外面有宝,赶紧捡回来按序放好,不能重样哟 有点像小夫妻俩,老公q在外面淘宝,找到后运回来,找到一个新的宝,老婆p在家里就给挖个新坑放好,最后外面没宝了,就结束咯

中间对话

老公:老婆,这个家里有没?(if) 老婆:有了。(nums[p] == nums[q])你再找找(q++)

老公:老婆,这个家里有没?(if) 老婆:有了。(nums[p] == nums[q])你再找找(q++)

老公:老婆,这个家里有没?(if) 老婆:这个没有,拿回来吧 (nums[p] != nums[q]) 放好了,我到下一个位置等你(p++) 你再继续找吧(q++)

貌似双指针都可以这么理解

相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素

int leftIndex = 0; int rightIndex = nums.size() - 1; while (leftIndex <= rightIndex) { // 找左边等于val的元素 while (leftIndex <= rightIndex && nums[leftIndex] != val){ ++leftIndex; } // 找右边不等于val的元素 while (leftIndex <= rightIndex && nums[rightIndex] == val) { -- rightIndex; } // 将右边不等于val的元素覆盖左边等于val的元素 if (leftIndex < rightIndex) { nums[leftIndex++] = nums[rightIndex--]; } } return leftIndex; // leftIndex一定指向了最终数组末尾的下一个元素

https://www.programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html#%E6%80%9D%E8%B7%AF

题目

双指针法,i指向起始位置,j指向终止位置。

定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。

如果A[i] * A[i] < A[j] * A[j] 那么result[k--] = A[j] * A[j]; 。

如果A[i] * A[i] >= A[j] * A[j] 那么result[k--] = A[i] * A[i]; 。

滑动窗口

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

在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。

那么滑动窗口如何用一个for循环来完成这个操作呢。

如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。

  • 如果只用一个for循环来表示 滑动窗口的起始位置,遍历剩下的终止位置难免再次陷入 暴力解法的怪圈。
  • 所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置

实现滑动窗口,主要确定如下三点:

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

题目:长度最小的子数组

在本题中

  • 窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
  • 窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
  • 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

var minSubArrayLen = function(target, nums) {     let sum = 0,         result = nums.length ,         j = 0,         i = 0,         subL = 0;     for (; j < nums.length; j ++) {         sum += nums[j]         while (sum >= target) {             subL = j - i + 1;             result = result < subL ? result : subL;             sum -= nums[i ++];         }     }     return result };

题目

/**  * @param {number[]} fruits  * @return {number}  */ var totalFruit = function(fruits) {     let l = 0;//起始指针     let maxLen = 0;//窗口的最大长度 其中最多包涵两种水果     let n = 0//前一类水果的结束位置     let arr = [fruits[l]]//水果的种类数组     console.log(arr)     for(let r = 0; r < fruits.length; r++){//窗口的右指针不断前进         if(!arr.includes(fruits[r])){//如果窗口中不包含 进窗口的水果             if(arr.length <= 1){//如果只有一种水果                 arr[1] = fruits[r]//将这种水果加入arr数组             }else{//如果有两种水果                 l = n//更新窗口的左边界                 arr[0] = fruits[r-1]//更新arr中水果的种类                 arr[1] = fruits[r]             }         }                 if(fruits[r] !== fruits[n]){//如果进来了一种新的类型的水果 更新前一种水果的位置             n = r         }         maxLen = Math.max(maxLen,r-l+1)//更新滑动窗口的最大值     }     return maxLen };

螺旋矩阵https://leetcode.cn/problems/spiral-matrix-ii/submissions/

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

坚持循环不变量原则

模拟顺时针画矩阵的过程:

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

由外向内一圈一圈这么画下去

每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来

math函数库中的一个函数,math.floor (x) 返回小于参数x的最大整数,即对浮点数向下取整。x[]的取值。

Array.fill

arr.fill(value[, start[, end]])

fill() 方法用一个固定值填充一个数组中,

从起始索引到终止索引内的全部元素,

不包括终止索引,

返回被修改后的数组。

value:用来填充数组元素的值。

start:起始索引,默认值为0。

end:终止索引,默认值为 this.length。

``` /**  * @param {number} n  * @return {number[][]}  */ var generateMatrix = function(n) {     let loop = Math.floor(n / 2);     let mid = Math.floor(n / 2);     let count = 1     let row = col = 0     let startX = startY = 0     let offset = 1     let res = new Array(n).fill(0).map(() => new Array(n).fill(0));

    while(loop --) {         row = startX         col = startY         for(; col < n - offset ; col ++)              res[row][col] = count ++

        for (; row < n - offset ; row ++)             res[row][col] = count ++

        for (; col > startY; col --)             res[row][col] = count ++

        for (; row > startX; row --)             res[row][col] = count ++                  startX ++         startY ++         offset += 1     }     if(n % 2 === 1)         res[mid][mid] = count           return res }; ```


题目: 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 https://leetcode.cn/problems/spiral-matrix/


猜你喜欢

转载自blog.csdn.net/weixin_50945128/article/details/129377822