数组理论基础
数组是非常基础的数据结构,在面试中,考察数组的题目一般在思维上都不难,主要是考察对代码的掌控能力。
也就是说,想法很简单,但实现起来 可能就不是那么回事了。
数组在内存中的存放方式:
数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便的通过下标索引的方式获取到下标对应的数据。
数组特征:
1、数组下标都是从0开始
2、数组的内存空间地址是连续的——>所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
在C++语言中,二维数组的地址是一行接着一行连续的;
在Java中,因为虚拟机对地址的封装导致我们没法实际看到地址的连续性,大概率猜测Java中二维数组地址是以行为单位连续,但是行与行之间不一定相连
3、数组的元素是不能删的,只能覆盖。
二分查找
1、边界定义很重要,左闭右开or左闭右闭要全程心中铭记,由此来思考循环中的条件是否可以相等、指针指向如何改变
2、计算middle中的/2操作可以用位运算来提速
3、需要注意二分的几种情况
(1)当l = 0, r = n的时候因为r这个值我们在数组中无法取到,while(l < r) 是正确写法
(2)当l = 0, r = n - 1的时候因为r这个值我们在数组中可以取到,while(l <= r) 是正确写法 主要看能不能取到这个值
4、二分的最大优势是在于其时间复杂度是O(logn),因此看到有序数组都要第一时间反问自己是否可以使用二分
5、关于为什么middle用到 left + (right - left) / 2 而不是 (left + right) / 2 是因为存在一个防溢出的情况:当mid = (l + r) / 2时,如果l + r 大于 INT_MAX(C++内,就是int整型的上限),那么就会产生溢出问题(int类型无法表示该数)
移除元素
1、暴力解法:当所有元素往前挪一位进行覆盖的时候,要把指针也往前移一位重新判断新移动过来的元素
2、双指针算法(快慢指针):一个指针指的是最终返回的没有指定元素的数组的最后一个元素下标,另一个指针去搜索不是指定元素的元素;也就是说,快慢指针在寻找非指定元素
3、双向指针法:将其中一个指针(一般是left指针)定义为指向最终返回的数组的最后一个元素的下标,同时判断此时指向元素是否为指定元素,另一个指针(right)不断寻找非指定元素,覆盖给left指针的指定元素,最终完成left指针指向下标之前的数组为所求数组,即全非指定元素数组
有序数组的平方
双指针法:数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
实现:i指向起始位置,j指向终止位置。
定义一个新数组result,和给定数组一样的大小,让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];
长度最小的子数组
滑动窗口法:不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果
螺旋矩阵
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。拐角处让给新的一条边来继续画。这也是坚持了每条边左闭右开的原则。