JavaScript算法——数组篇

数组理论基础

定义:数组是存放在连续内存空间上的相同类型数据的集合。

注意:

  1. 数组下标都是从0开始的
  2. 数组内存空间的地址是连续的

举例:

在这里插入图片描述
因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。

举例:

在这里插入图片描述

二分查找

限制:

  1. 数组为有序数组
  2. 数组无重复元素

二分查找

/*
	二分法
*/
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)

x的平方根

/*
	二分法
*/
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)

螺旋矩阵2

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)

总结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jackson Mseven

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值