【代码随想录】数组部分总结

二分查找

主要就是搞清楚是左闭右闭,还是左闭右开。两种思路。
二分查找那个题感觉不难,难的是应用二分查找方法进行各种变形。

704 二分查找题目代码

版本一 左闭右闭

var search = function(nums, target) {
    // right是数组最后一个数的下标,num[right]在查找范围内,是左闭右闭区间
    let mid, left = 0, right = nums.length - 1;
    // 当left=right时,由于nums[right]在查找范围内,所以要包括此情况
    while (left <= right) {
        // 位运算 + 防止大数溢出
        mid = left + ((right - left) >> 1);
        // 如果中间数大于目标值,要把中间数排除查找范围,所以右边界更新为mid-1;如果右边界更新为mid,那中间数还在下次查找范围内
        if (nums[mid] > target) {
            right = mid - 1;  // 去左面闭区间寻找
        } else if (nums[mid] < target) {
            left = mid + 1;   // 去右面闭区间寻找
        } else {
            return mid;
        }
    }
    return -1;
};

(版本二)左闭右开区间 [left, right)

var search = function(nums, target) {
    // right是数组最后一个数的下标+1,nums[right]不在查找范围内,是左闭右开区间
    let mid, left = 0, right = nums.length;    
    // 当left=right时,由于nums[right]不在查找范围,所以不必包括此情况
    while (left < right) {
        // 位运算 + 防止大数溢出
        mid = left + ((right - left) >> 1);
        // 如果中间值大于目标值,中间值不应在下次查找的范围内,但中间值的前一个值应在;
        // 由于right本来就不在查找范围内,所以将右边界更新为中间值,如果更新右边界为mid-1则将中间值的前一个值也踢出了下次寻找范围
        if (nums[mid] > target) {
            right = mid;  // 去左区间寻找
        } else if (nums[mid] < target) {
            left = mid + 1;   // 去右区间寻找
        } else {
            return mid;
        }
    }
    return -1;
};
35.搜索插入位置
  • 大家注意这道题目的前提是数组是有序数组,这也是使用二分查找的基础条件。
    以后大家只要看到面试题里给出的数组是有序数组,都可以想一想是否可以使用二分法
    同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的。

  • 可以按照情况进行分析,比如采用左闭右开还是左闭右闭,在这个前提条件下,分析可能的几种情况,注意特殊情况的思考与探讨。然后验证程序是不是满足所有情况

  • 左闭右闭的区间里,大家要仔细看注释,思考为什么要写while(left <= right), 为什么要return right+1
    因为跳出循环的条件是right<left,所以此时right<left。所以right才是那个小的值,所以要right+1,或者left.

  • 左闭右开的区间里,大家要仔细看注释,思考为什么要写while(left <= right), 为什么要return right
    因为跳出循环的条件是right<=left,所以此时right=left。而且right是一直没有取到的那个值,所以输出right即可,left也可以。

34. 在排序数组中查找元素的第一个和最后一个位置
  • 写两个二分分别找左边界和右边界
  • 还是注意分别讨论情况
  • 根据上面的经验,寻找左边界的是right,寻找右边界的是left.
  • 想不明白的时候带入例子
  • 注意每道题比较的是什么,返回的是什么,之前返回的都是mid,这次返回的是left和right
69.x 的平方根
  • 采用二分查找的思想,查找的中间整数,来检验其平方与目标值的大小对比。
  • 别一下子合并,几种情况慢慢分析。
  • 因为舍去了小数部分,所以在不直接得到等于mid的情况下,采取mid*mid < x下的mid值作为最终值。
367.有效的完全平方数

完美做出

双指针法(快慢指针法):

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

定义快慢指针

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

27.移除元素
  • 问题关键在于需要原地移除元素,所以需要在一个数组内进行操作。
  • 注意这里只需要得到移除元素后的数组长度,并没有要求得到数组。
26.删除排序数组中的重复项
  • 主要考虑这行代码所涉及到的条件判断

if(nums[i] != nums[i+1])

283.移动零
  • 这个题主要还是考虑上面那个条件判断
  • 但是需要注意的是,这次返回的不是数组的长度,而是数组的内容。但是数组内容修改其实只修改到慢指针所在的位置。后面数组的内容其实没有被修改,所以需要重新考虑。
844.比较含退格的字符串
  • 其实是两个双指针
    注意:
    1.不要害怕double代码,像之前二分法就重复使用两次来找到左右边界,这个可以用两个双指针
    2.还是注意那个条件判断,在什么条件下指针更新呢,慢指针代表什么意思
    3,大循环套着小循环,这个也是,不要怕麻烦。也可以就是说两种情况下的双指针搞完了,看各种情况应该怎么总的处理。
    4.有个新的思路非常好,就是去除数组值不是remove等等,而是改变i和j的指向,直接对下面的数组值进行处理,相当于去除了某些值。达到了同样的效果

  • 也可以用栈来解决

977.有序数组的平方
  • 这道题直接暴力破解比较容易,但是要考虑复杂度等问题
  • 数组其实是有序的, 只不过负数平方之后可能成为最大数了。

那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。

此时可以考虑双指针法了,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循环来完成这个操作呢。

209.长度最小的子数组

1.可以使用暴力解法,利用两个循环构建起滑动窗口,求解。
用两个循环寻找在不同起始距离下满足条件的最短长度。
注意:一旦满足长度就跳出当前的小循环
要不要继续循环,考虑继续循环后得到的结果还有意义吗?(更长的长度,没有意义)
每次循环前都要清空sum值:要考虑每次循环的初始条件

即:开始每次循环的条件和结束每次循环的条件

2.也可以使用滑动·窗口,即使用一个for循环

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

结束位置的移动主要用于得到数据相加大于目标值的位置
起始位置的移动主要是为了找到大于目标值时最小的距离
注意考虑特殊情况,比如没有满足的条件,输出结果为0
以及所有数字相加才满足条件的情况。
以后做算法题时都要先考虑特殊情况

904.水果成篮

用到哈希表

76.最小覆盖子串

用到哈希表

螺旋矩阵

59.螺旋矩阵2
  • 坚持循环不变量原则,坚持左闭右开
  • 一条边一条边怎么走的,怎么循环的,慢慢写出来,不要怕麻烦
  • 考虑转圈的次数:循环次数(转的圈数)是n/2:如果转一圈,左右都会少一条边,整个正方形的宽度减2,一共宽度是n,每次减2,就是循环n/2次
  • 考虑每次转圈后的变量,offset
  • 考虑特殊情况,n为奇数时中心点是哪个。
54.螺旋矩阵
剑指Offer 29.顺时针打印矩阵

这两个题是前面一个题的反顺序操作。

总结

在这里插入图片描述
可能因为js没有整型的原因,经常会用到math.floor来保证为整数。

时间复杂度和空间复杂度
时间复杂度

所以我们说的时间复杂度都是省略常数项系数的,是因为一般情况下都是默认数据规模足够的大,基于这样的事实,给出的算法时间复杂的的一个排行如下所示:

在这里插入图片描述
递归算法的时间复杂度本质上是要看: 递归的次数 * 每次递归中的操作次数。

空间复杂度

递归算法的空间复杂度 = 每次递归的空间复杂度 * 递归深度

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值