977.有序数组的平方
暴力解法:数组内部先平方,再排序
- js自带sort排序函数
var sortedSquares = function(nums) {
var arr = nums.map((item) => item*item)
arr.sort((a,b) => a-b)
return arr
};
- 平方+快排
快速排序用递归法若数组长度大,栈的内存不够,容易爆内存溢出错误,报错Last few GCs
var sortedSquares = function(nums) {
var arr = nums.map((item) => item*item)
return quickSort(arr)
};
var quickSort = function(arr){
if(arr.length<=1) return arr;
let target = arr[0]
let left = [], right = []
let same_target = []
for(let i=0; i<arr.length; i++){
if(arr[i]<target) left.push(arr[i]);
else if(arr[i]>target) right.push(arr[i]);
else same_target.push(arr[i])
}
return quickSort(left).concat(same_target,quickSort(right))
}
- 平方+简单选择排序
平方+排序 时间复杂度O(n+n^2) 最后时间复杂度为O(n^2)
但是这只是其中一种排序,最有效的排序时间复杂度为O(nlogn),加上前面遍历数组平方的时间复杂度O(n)后为O(n+nlogn),最后时间复杂度为O(nlogn)
var sortedSquares = function(nums) {
var arr = nums.map((item) => item*item)
return selecedSort(arr)
};
var selectedSort = function(arr){
for(let i=0; i<arr.length; i++){
var minIndex = i
for(var j=i+1; j<arr.length; j++){
if(arr[j]<arr[minIndex]){
[arr[minIndex],arr[j]] = [arr[j],arr[minIndex]]
}
}
}
return arr
}
双指针解法
双指针法:数组已经有序了,平方之后还需有序,就是要考虑正负绝对值大小问题
关键点:新数组的大端,只能由原数组两端对比大小得出,依次两头对比(类比快排),不可能是中间。这种形式,暗喻了可以用双指针,前后各定义一个指针。
时间复杂度O(n)
var sortedSquares = function(nums) {
let arr = new Array(nums.length).fill(0)
let [left, right] = [0, nums.length-1]
let k = arr.length-1
while (left <= right) {
let val_l = Math.pow(nums[left], 2)
let val_r = Math.pow(nums[right], 2)
if (val_r > val_l) {
arr[k--] = val_r
right--
} else {
arr[k--] = val_l
left++
}
}
return arr
};
209.长度最小的子数组
暴力解法:i,j分别为定头和定尾指针 (数组太大超时)
i外层循环依次取[0,nums.length-1],j从头开始遍历到符合条件的最小长度数组
记录每次符合条件的子串长度j-i+1 (j从i值取值比较统一且方便),最后取最小值
时间复杂度为O(n^2)
var minSubArrayLen = function(target, nums) {
let sum = 0, subLength = 0, min = Infinity
for(let i=0; i<nums.length; i++){
sum = 0
for(let j=i; j<nums.length; j++){
sum += nums[j]
if(sum>=target){
subLength = j-i+1
// 符合条件最小连续子数组,跳出该循环
break
}
}
min = Math.min(subLength,min)
}
return min===Infinity?0:min
};
滑动窗口的由来
暴力解法的双指针,i从0开始遍历,j从i开始遍历,依次找的是以下标为0~nums.length-1为起始位置的符合sum>=target的最小子数组
比如从第一个数组元素开始的向后四个元素为第一轮最小符合子数组;第二轮从第二个元素开始遍历计算sum,但是其实第2~4的元素第一轮已经遍历过一次且计算过sum了;之后每轮循环都会有一部分与上一循环重复遍历且计算,这样来回折腾,浪费时间
O(n)滑动窗口方法
优化了时间复杂度,变相双指针用一个for循环解决两个for循环事件
rightwindow为终结位置指针(若为起始位置指针,梦回暴力解法),leftwindow为起始位置指针
leftwindow指针努力压缩长度,直到不符合条件,rightwindow努力符合条件(你追我赶)
每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)
var minSubArrayLen = function(target, nums) {
let minLength = Infinity
let leftWindow = 0
let currentSum = 0
for (let rightWindow = 0; rightWindow <= nums.length; rightWindow++) {
currentSum += nums[rightWindow]
// 这里是while,不是if,可能需要left连续向右移动好几次才不符合条件
while (currentSum >= target) {
minLength = Math.min(minLength, rightWindow - leftWindow + 1)
currentSum -= nums[leftWindow]
leftWindow++
}
}
return minLength === Infinity ? 0: minLength
};
59.螺旋矩阵II
var generateMatrix = function(n) {
let matrix = Array.from(Array(n),() => Array(n));
let top = 0,bottom = n-1,left = 0,right = n-1;
let element = 1;
while(top <= bottom && left <= right){
for(let i = left;i <= right; i++){
matrix[top][i] = element++;
}
top++;
for(let i = top;i <= bottom; i++){
matrix[i][right] = element++;
}
right--;
for(let i = right; i>= left; i--){
matrix[bottom][i] = element++;
}
bottom--;
for(let i = bottom;i >= top; i--){
matrix[i][left] = element++;
}
left++;
}
return matrix;
};