1.数组理论基础
数组是存放在连续内存空间上的相同类型数据的集合
数组元素为相同类型的元素(同一类型元素所需要的存储空间大小一致,所以方便利用元素的索引来计算元素所在的位置)
数组可以通过下标索引的方式获取到下标对应的数据。
注意:
- 数组的下标是从0 开始的
- 数组内存空间的地址是连续的
因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
数组的元素是不能删的,只能覆盖。
二维数组在内存的空间地址是否连续:
在C++中二维数组在地址空间上是连续的;
但其他语言中二维数组在地址空间上不一定连续。
JavaScript中的数组:
同一个JavaScript数组的元素的数据类型可以不同,每个元素所需要的存储空间的大小不能确定,则数组没有办法通过元素的索引来计算某个元素对应的存储地址。
JavaScript的数组可以任意更改大小。
2.二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
实例:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
前提条件:数组是有序数组,且数组中无重复元素(二分法的前提条件)
使用二分法时需要注意区间的定义,区间的定义为不变量。
区间的定义:左闭右闭 [left,right] 或左闭右开 [left,right)
二分法一
目标值target在左闭右闭的区间 [left, right]
注意:
while(left<=right) left==right时有意义
if(nums[middle]>target) right=middle-1; 目标值位于左区间
var search = function(nums, target) {
let l = 0, r = nums.length - 1;
// 区间 [l, r]
while(l <= r) {
let mid = (l + r) >> 1;
if(nums[mid] === target) return mid;
let isSmall = nums[mid] < target;
l = isSmall ? mid + 1 : l;
r = isSmall ? r : mid - 1;
}
return -1;
};
二分法二
目标值target在左闭右开的区间内 [left,right)
注意:
while (left<right) left==right时无意义
if(nums[middle]>target) right=middle; 目标值位于左区间
var search = function(nums, target) {
let left=0,right=nums.length;
while(left<right){
let mid=(left+right)>>1;
if(nums[mid]==target)
return mid;
left=nums[mid]<target ? mid+1 :left;
right=nums[mid]<target ? right : mid;
}
return -1;
};
3.移出元素
LeetCode
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。
暴力解法:
两层for循环,一个for循环遍历数组,查看是否有元素等于指定值,第二个for循环用于更新数组
var removeElement = function(nums, val) {
var len=nums.length;
for(var i=0;i<len;i++){
if(nums[i]==val){
for(var j=i+1;j<len-i;j++){
nums[j-1]=nums[j];
}
i--;
len--;
}
return len;
}
};
双指针法:
(快慢指针法)通过快慢指针在一个for循环下完成两个for循环的工作。
慢指针寻找与给定值相同的元素,通过快指针更新数组的值。
function remove(nums,val){
var slowIndex=0;
for(var fastIndex=0;fastIndex<nums.length;fastIndex++){ //快指针用于遍历数组
if(nums[fastIndex]!=val){ //若快指针对应的元素值等于给定值
nums[slowIndex++]=nums[fastIndex]; //则快指针的值等于快指针的值(通过slowIndex++来移动安慢指针)
} //若快指针的值与给定值相等,则慢指针处于原位置不动,当快指针移动到与给定值不相等的位置时,再将其赋值给慢指针,替换掉原相等的值,即移除该元素
}
console.log(slowIndex); //慢指针指向的是移除相等元素后数组的元素
};
remove([3,2,2,3,5,3,9,6],3);
4.有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
暴力排序
将数组中的元素直接平方再进行排序
//暴力排序
var sortedSquares=function(nums){
for(var i=0;i<nums.length;i++){
var a=nums[i]*nums[i];
}
nums.sort();
console.log(nums);
}
sortedSquares([4,7,2,8,1]);
双指针法
因为数组是按从大到小的顺序来排序的,最大值和最小值都排列在数组的两端,最小值可能为负数,因此平方后的值两边的一定大于中间的。
定义双指针i和j, 定义一个和原数组大小相同的空数组,比较位于原数组两端元素的大小,将大的从尾部开始放入空数组中。
//双指针法
var sortedSquares=function(nums){
var i=0,j=nums.length-1;
var result=[];
var k=nums.length-1;
while(i<=j){
if(nums[i]*nums[i]>=nums[j]*nums[j]){
// if(Math.abs(nums[i])>=Math.abs(nums[j])){
result[k]=nums[i]*nums[i];
k--;
i++;
}else{
result[k]=nums[j]*nums[j];
k--;
j--;
}
}
console.log(result);
}
sortedSquares([-4,-1,3,6,9]);
5.长度最小的子数组
LeetCode
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
暴力解法
滑动窗口
不断调节子序列的起始位置和末位置,找到符合条件的最短距离。
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,窗口的起始位置设置为数组的起始位置就可以了。
var minArray=function(nums,s){
var i=0,j=0,sum=0,result=nums.length+1; //子数组的长度不可能超过原数组的长度
while(i<nums.length){
sum+=nums[i]; //滑动窗口的末位置
while(sum>=s){ //当子数组长度大于等于给定值时
result=(i-j+1)<result?(i-j+1):result; //最终子数组的长度(最小长度)
sum-=nums[j]; //滑动窗口起始位置向右滑动
j++;
}
i++;
}
console.log(result>nums.lenght ? 0 : result); //若子数组的长度仍然大于原数组长度,则证明未改变,没有符合结果,返回0
}
minArray([1,5,2,1,3,9],9);
6.螺旋矩阵II
leetcode
给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
var generateMatrix = function(n) {
// new Array(n).fill(new Array(n))
// 使用fill --> 填充的是同一个数组地址
const res = Array.from({length: n}).map(() => new Array(n));
let loop = n >> 1, i = 0, //循环次数
count = 1,
startX = startY = 0; // 起始位置
while(++i <= loop) {
// 定义行列
let row = startX, column = startY;
// [ startY, n - i)
while(column < n - i) {
res[row][column++] = count++;
}
// [ startX, n - i)
while(row < n - i) {
res[row++][column] = count++;
}
// [n - i , startY)
while(column > startY) {
res[row][column--] = count++;
}
// [n - i , startX)
while(row > startX) {
res[row--][column] = count++;
}
startX = ++startY;
}
if(n & 1) {
res[startX][startY] = count;
}
return res;
};