代码随想录打卡(数组篇)

198 阅读6分钟

在前端开发中,算法能力是工程师必备的核心技能之一。

题目一:二分查找(左闭右闭区间)

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的索引。如果不存在,则返回 -1。

var search = function(nums, target) {
    let mid, left = 0, right = nums.length - 1; // 初始化左右指针,right为数组最后一个元素的索引
    while (left <= right) { // 当left等于right时,仍然需要检查该元素是否为目标值
        mid = left + ((right - left) >> 1); // 防止溢出,计算中间索引
        if (nums[mid] > target) { // 如果中间元素大于目标值,说明目标值在左半部分
            right = mid - 1; // 调整右指针,缩小搜索范围到左半部分
        } else if (nums[mid] < target) { // 如果中间元素小于目标值,说明目标值在右半部分
            left = mid + 1; // 调整左指针,缩小搜索范围到右半部分
        } else { // 找到目标值,返回其索引
            return mid;
        }
    }
    return -1; // 未找到目标值,返回-1
};

解题思路:在左闭右闭区间 [left, right] 中查找目标值。每次计算中间位置 mid,根据中间值与目标值的大小关系,调整搜索范围。如果找到目标值,直接返回索引;否则,返回 -1。

题目二:二分查找(左闭右开区间)

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的索引。如果不存在,则返回 -1。

var search = function(nums, target) {
    let mid, left = 0, right = nums.length; // 初始化左右指针,right为数组长度,表示左闭右开区间
    while (left < right) { // 当left等于right时,说明搜索范围为空
        mid = left + ((right - left) >> 1); // 防止溢出,计算中间索引
        if (nums[mid] > target) { // 如果中间元素大于目标值,说明目标值在左半部分
            right = mid; // 调整右指针,缩小搜索范围到左半部分,由于是左闭右开区间,所以不包含mid
        } else if (nums[mid] < target) { // 如果中间元素小于目标值,说明目标值在右半部分
            left = mid + 1; // 调整左指针,缩小搜索范围到右半部分
        } else { // 找到目标值,返回其索引
            return mid;
        }
    }
    return -1; // 未找到目标值,返回-1
};

解题思路:在左闭右开区间 [left, right) 中查找目标值。通过调整 right 指针的位置,逐步缩小搜索范围,直到找到目标值或搜索范围为空。

题目三:移除数组中的元素

给定一个数组 nums 和一个值 val,你需要原地移除所有等于 val 的元素,并返回移除后数组的新长度。

var removeElement = (nums, val) => {
    let k = 0; // 初始化新数组长度
    for(let i = 0; i < nums.length; i++){ // 遍历数组元素
        if(nums[i] != val){ // 如果当前元素不等于val
            nums[k++] = nums[i]; // 将当前元素移动到新数组对应位置,并增加新数组长度
        }
    }
    return k; // 返回新数组长度
};

解题思路:通过双指针法,一个指针用于遍历数组,另一个指针用于记录非目标值元素的位置。将非目标值元素依次填充到数组前面,最终返回非目标值元素的个数。

题目四:有序数组的平方

给定一个按非递减顺序排序的整数数组 nums,返回每个数字的平方组成的数组,要求也按非递减顺序排序。

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; // 返回结果数组
};

解题思路:利用双指针从两端向中间遍历数组,比较两端元素的平方值,将较大的平方值依次存入结果数组的末尾。这样可以避免排序操作,提高效率。

题目五:最小长度子数组

给定一个正整数数组 nums 和一个正整数 target,找到该数组中满足其和 ≥ target 的最小长度子数组。

var minSubArrayLen = function(target, nums) {
    let start = 0, end = 0; // 初始化滑动窗口的左右指针
    let sum = 0; // 当前窗口内元素的和
    let len = nums.length; // 数组长度
    let ans = Infinity; // 初始化最短长度为无穷大
    
    while(end < len){ // 当右指针未超出数组长度时,继续扩大窗口
        sum += nums[end]; // 将当前元素加入窗口
        while (sum >= target) { // 当窗口和满足条件时,尝试缩小窗口
            ans = Math.min(ans, end - start + 1); // 更新最短长度
            sum -= nums[start]; // 移除左指针元素,缩小窗口
            start++; // 移动左指针
        }
        end++; // 移动右指针,扩大窗口
    }
    return ans === Infinity ? 0 : ans; // 如果未找到满足条件的子数组,返回0;否则返回最短长度
};

解题思路:使用滑动窗口技术,通过两个指针 start 和 end 控制窗口的大小。不断扩大窗口直到窗口内的元素和大于等于目标值,然后尝试缩小窗口以找到更小的满足条件的子数组。

题目六:螺旋矩阵

给定一个正整数 n,生成一个包含 1 到 n² 所有元素且元素按顺时针顺序螺旋排列的 n x 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 < n - offset; col++) {
            res[row][col] = count++;
        }
        // 填充右列,从上到下
        for (; row < 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 += 1;
    }
    // 如果n为奇数,单独填充中间元素
    if (n % 2 === 1) {
        res[mid][mid] = count;
    }
    return res; // 返回结果矩阵
};

解题思路:通过模拟螺旋填充的过程,从外向内逐层填充矩阵。使用循环控制每一层的填充,每次填充后调整起始位置和偏移量,直到完成整个矩阵的填充。