数组理论基础
定义:数组是存放在连续内存空间上的相同类型数据的集合。
注意:
- 数组下标都是从0开始的
- 数组内存空间的地址是连续的
举例:
因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
举例:
二分查找
限制:
- 数组为有序数组
- 数组无重复元素
/*
二分法
*/
var search = function (nums, target) {
let left = 0
let right = nums.length - 1
while(left <= right) {
let mid = left + ((right - left) >> 1) // 移位运算,效率比 /2 更快
if(nums[mid] === target) return mid
else if(nums[mid] > target) right = mid - 1
else left = mid + 1
}
return -1
};
- 时间复杂度:O(logn)
- 空间复杂度:O(1)
/*
二分法
*/
var searchInsert = function(nums, target) {
let left = 0
let right = nums.length - 1
while(left <= right) {
let mid = left + ((right - left) >> 1) // 移位运算,效率比 /2 更快
if(nums[mid] === target) return mid
else if(nums[mid] > target) right = mid - 1
else left = mid + 1
}
return right + 1
};
- 时间复杂度:O(logn)
- 空间复杂度:O(1)
/*
二分法
*/
var searchRange = function(nums, target) {
let leftBorder = searchLeftBorder(nums, target)
let rightBorder = searchRightBorder(nums, target)
// target 大于或小于数组中的所有数
if(leftBorder === -2 || rightBorder === -2) return [-1, -1]
// target 存在
if(rightBorder - leftBorder > 1) return [leftBorder + 1, rightBorder - 1]
// target 不存在,但是介于数组中的某两个数之间
return [-1,-1]
};
var searchLeftBorder = function(nums, target) {
let left = 0
let right = nums.length - 1
let leftBorder = -2 // 左边界(不包含 target)
while(left <= right) {
let mid = left + ((right - left) >> 1) // 移位运算,效率比 /2 更快
if(nums[mid] < target) left = mid + 1
else {
right = mid - 1
leftBorder = right
}
}
return leftBorder
}
var searchRightBorder = function(nums, target) {
let left = 0
let right = nums.length - 1
let rightBorder = -2 // 有边界(不包含 target)
while(left <= right) {
let mid = left + ((right - left) >> 1)
if(nums[mid] > target) right = mid - 1
else {
left = mid + 1
rightBorder = left
}
}
return rightBorder
}
- 时间复杂度:O(logn)
- 空间复杂度:O(1)
/*
二分法
*/
var mySqrt = function(x) {
if(x < 2) return x
let left = 0
let right = x >> 1 // 移位运算,效率比 /2 更快
while(left <= right) {
let mid = left + ((right - left) >> 1)
if(Math.pow(mid, 2) === x) return mid
else if(Math.pow(mid, 2) > x) right = mid - 1
else left = mid + 1
}
return right
};
- 时间复杂度:O(logn)
- 空间复杂度:O(1)
/*
二分法
*/
var isPerfectSquare = function(num) {
if(num < 2) return true
let left = 0
let right = num >> 1 // 移位运算,效率比 /2 更快
while(left <= right) {
let mid = left + ((right - left) >> 1)
if(Math.pow(mid, 2) === num) return true
else if(Math.pow(mid, 2) > num) right = mid - 1
else left = mid + 1
}
return false
};
- 时间复杂度:O(logn)
- 空间复杂度:O(1)
双指针
/*
es6 简便解法
*/
var removeElement = function(nums, val) {
while(nums.indexOf(val) != -1) {
nums.splice(nums.indexOf(val), 1)
}
return nums.length
};
/*
双指针解法
*/
var removeElement = function(nums, val) {
// left:左指针,right:右指针
let left = 0, right = nums.length;
while(left < right) {
// 元素的数值等于val,将数组最后的一个元素设置到当前位置
if(nums[left] === val) {
nums[left] = nums[right - 1];
right --;
} else left ++;
}
return left;
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
/*
双指针解法
*/
var removeDuplicates = function(nums) {
// 如果数组只有一个元素
if(nums.length === 1) return 1;
// left:左指针,right:右指针
let left = 0, right = 1;
while(right < nums.length) {
// 左、右指针的元素不同,将右指针的元素
if(nums[left] !== nums[right]) nums[++left] = nums[right];
right++;
}
return left + 1;
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
/*
双指针解法
*/
var moveZeroes = function (nums) {
let left = right = 0
while(right < nums.length) {
if(nums[right] !== 0) {
let temp = nums[left]
nums[left] = nums[right]
nums[right] = temp
left ++
}
right ++
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
/*
双指针解法
*/
var backspaceCompare = function(s, t) {
return parseString(s) === parseString(t)
};
var parseString = function(str) {
let left = 0
let right = 0
let strArr = str.split("")
while(right < str.length) {
if(strArr[right] !== "#"){
strArr[left] = strArr[right]
left++
}else if(left !== 0){
left--
}
right++
}
strArr.length = left
return strArr.join("")
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
/*
双指针解法
*/
var sortedSquares = function(nums) {
let n = nums.length;
let res = new Array(n).fill(0);
let i = 0, j = n - 1, k = n - 1;
while (i <= j) {
let left = nums[i] * nums[i],
right = nums[j] * nums[j];
if (left < right) {
res[k--] = right;
j--;
} else {
res[k--] = left;
i++;
}
}
return res;
};
- 时间复杂度:O(n)
- 空间复杂度:O(n)
滑动窗口
/*
滑动窗口
*/
var minSubArrayLen = function (target, nums) {
let result = nums.length + 1 // 最终结果
let sum = 0 // 滑动窗口的和
let i = 0 // 滑动窗口起始位置
for(let j = 0; j < nums.length; j++) {
sum += nums[j]
while(sum >= target) {
result = result < j - i + 1 ? result : j - i + 1
sum -= nums[i++]
}
}
return result === nums.length + 1 ? 0 : result
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
/*
滑动窗口
*/
var totalFruit = function(fruits) {
const map = new Map();
let left = 0 // 滑动窗口左指针
let result = 0 // 最终结果
for (let right = 0; right < fruits.length; right++) {
map.set(fruits[right], (map.get(fruits[right]) || 0) + 1);
while (map.size > 2) {
map.set(fruits[left], map.get(fruits[left]) - 1);
if (map.get(fruits[left]) === 0) {
map.delete(fruits[left]);
}
left++;
}
result = result > right - left + 1 ? result : right - left + 1;
}
return result;
};
- 时间复杂度:O(n)
- 空间复杂度:O(n)
/*
滑动窗口
*/
var minWindow = function (s, t) {
let left = 0 // 滑动窗口左边界
let right = 0 // 滑动窗口右边界
const need = new Map()
for(const value of t) {
need.set(value, need.has(value) ? need.get(value) + 1 : 1) // 向need中填充子字符串个字符的数量
}
let needType = need.size // need的长度
let res = '' // 返回字符串
while(right < s.length) {
let item = s[right] // 父字符串的字符
if(need.has(item)) { // 如果need中存在这个字符
need.set(item, need.get(item) - 1) // 将need中这个字符的数量-1
if(need.get(item) === 0) needType -= 1 // 如果need中这个字符的数量为0,则needType -1
}
while(needType === 0) { // 当need中所有的字符都没有了
const newRes = s.substring(left, right + 1) // 创建临时的返回字符串
if(!res || newRes.length < res.length) res = newRes // 如果临时的返回字符串的长度小于返回字符串,则进行替换
const item2 = s[left] // 父字符串的左边界的字符
if(need.has(item2)) { // 如果need中有这个字符
need.set(item2, need.get(item2) + 1) // 将need中这个字符的数量+1
if(need.get(item2) === 1) needType += 1 // 如果这个字符的数量为1,则needType +1
}
left += 1
}
right += 1
}
return res
};
- 时间复杂度:O(m + n)
- 空间复杂度:O(n)
模拟行为
var spiralOrder = function(matrix) {
let x = y = 0; // 起始位置
let bool = Array.from({length: matrix.length}, _ => new Array(matrix[0].length).fill(0)); // 判断边界的矩阵
let diraction = 0; // 0——向右;1——向下;2——向左;3——向上
let res = []; // 最终结果
for(let i = 0; i < matrix.length * matrix[0].length; i++) {
res.push(matrix[x][y]); // 向返回的数组中填充值
bool[x][y] = 1; // 设置边界矩阵
if(diraction == 0) {
if(y != matrix[0].length - 1 && bool[x][y + 1] == 0) {
y++;
} else {
diraction = 1;
x++;
}
} else if(diraction == 1) {
if(x != matrix.length - 1 && bool[x + 1][y] == 0) {
x++;
} else {
diraction = 2;
y--;
}
} else if(diraction == 2) {
if(y != 0 && bool[x][y - 1] == 0) {
y--;
} else {
diraction = 3;
x--;
}
} else {
if(x != 0 && bool[x - 1][y] == 0) {
x--;
} else {
diraction = 0;
y++;
}
}
}
return res;
};
- 时间复杂度:O(m × n)
- 空间复杂度:O(m × n)
var generateMatrix = function(n) {
let startX = startY = 0; // 起始位置
let loop = Math.floor(n/2); // 旋转圈数
let mid = Math.floor(n/2); // 中间位置
let offset = 1; // 控制每一层填充元素个数
let count = 1; // 更新填充数字
let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
while (loop--) {
let row = startX, col = startY;
// 上行从左到右(左闭右开)
for (; col < startY + n - offset; col++) {
res[row][col] = count++;
}
// 右列从上到下(左闭右开)
for (; row < startX + 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
offset += 2;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2 === 1) {
res[mid][mid] = count;
}
return res;
};
- 时间复杂度:O(n2)
- 空间复杂度:O(n2)