02数据结构_数组&LeetCode485题/283题/27题

129 阅读3分钟

数据结构_数组&LeetCode485题/283题/27题

一、数组类型

1.什么是数组

数组,是在连续的内存空间中,存储一组相同类型的元素

2.数组操作的空间复杂度

操作空间复杂度原因
访问AccessO(1)通过索引下标,经过计算可得地址,即可直接访问
搜索SearchO(N)最坏的情况下所有的元素都要移动位置,相当于遍历一遍
插入InsertO(N)需要将后面的元素往后移动
如果内存不够,需要开辟一块新空间,将数组移进去
删除DeleteO(N)需要将后面元素往前移

3.数组的特点

适合读,不适合写

适合场景: 读多写少

4.数组常用操作

具体语言可查看文档

  1. 创建数组

  2. 添加元素

  3. 访问元素

  4. 修改元素

  5. 删除元素

  6. 遍历数组

  7. 查找元素

  8. 数组的长度

  9. 数组排序(内置的排序方法)

附:JavaScript中数组的特点

  1. JavaScript数组中的默认存储值是undefined,其它编程语言数组的默认存储值是0或者是垃圾数据

  2. 与其它的编程语言不同,JavaScript可以访问数组中不存在的索引,会返回undefined,而其它的编程语言会报错或返回垃圾数据

  3. JavaScript可以存储不同类型的数据,而其它的编程语言只能存储一种数据类型的数据

  4. 当JavaScript中数组的存储空间不够用时,它会自动扩容,而其它的语言数组的大小是固定的,一旦定义了,就无法改变

  5. JavaScript中分配给数组的存储空间是不连续的,而其他编程语言中分配给数组的存储空间是连续的

二、练习题

1.LeetCode485题

题目描述:

485. 最大连续 1 的个数

难度简单292收藏分享切换为英文接收动态反馈

给定一个二进制数组 nums , 计算其中最大连续 1 的个数。

示例 1:

输入:nums = [1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.

示例 2:

输入:nums = [1,0,1,1,0,1]
输出:2

提示:

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1.

做题思路:

因为数组中除了0就是1,因此连续的1是被0所分隔的,

  1. 所以可通过for循环遍历数组,
  2. 当遇到1时:count计数,
  3. 遇到0时:判断count与result的大小,取出大的存储,将count归零再进行循环,
  4. 最后数组结尾的时候不一定有0,这种情况就无法进入判断,所以要将此时的count与result进行判断大小,返回大的

一开始的错误:

        let count = 0;
        let result = 0;
        let arrays = [1, 1, 0, 1, 1, 1];
        for (let i = 0; i < arrays.length; i++) {
            if (arrays[i] === 1) {
                count++;
            }
            else {
                //错误在此处,当遇到0时,直接将count的值赋值给result,没有进行大小的判断
                //如果arrays为[1, 1, 1, 1, 0, 1, 0, 1, 1],则第一次result为4,第二次result被修改为1
                //最后count为2,结果就是判断1与2的大小,返回2,但正确结果应为4。
                result = count;  
                count = 0;
            }
        }
        if (result > count) {
            console.log(result);
        }
        else {
            console.log(count);
        }

修改答案:

        let count = 0;
        let result = 0;
        let arrays = [1, 1, 0, 1, 1, 1];
        for (let i = 0; i < arrays.length; i++) {
            if (arrays[i] === 1) {
                count++;
            }
            else {
                if (result < count) {
                    result = count;
                }
                count = 0;
            }
        }
        if (result > count) {
            console.log(result);
        }
        else {
            console.log(count);
        }

2.LeetCode283题

题目描述:

283. 移动零

难度简单1427收藏分享切换为英文接收动态反馈

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

示例 2:

输入: nums = [0]
输出: [0]

提示:

  • 1 <= nums.length <= 104
  • -231 <= nums[i] <= 231 - 1

**进阶:**你能尽量减少完成的操作次数吗?

我的思路:

遍历数组,一旦遇到0,就删除它,然后利用count计数,遍历完毕再将count个0添加到数组最后

var moveZeroes = function(nums) {
        let count = 0;
        let j = 0;
        for (let i = 0;i < nums.length;i++) {
            if (nums[i] === 0) {
                count++;
                nums.splice(i,1);
                i = i - 1;
            }
        }
        while (j < count) {
            nums.push(0);
            j++;
        }
};
遇到bug:

splice 方法会直接对数组进行修改,不需再自己写一个算法来移动数组中的其他元素填补到被删除的位置,因此在一个for循环中加入了 splice 后,就会出现bug,以下为例:

//定义一个数组[0, 1, 2, 2, 4, 5],在for循环中,当元素为2时,利用splice删除该元素。
//我们预想的结果是[0, 1, 4, 5],但实际结果为[0, 1, 2, 4, 5]
//因为在i=2的循环中,数组为[0, 1, 2, 2, 4, 5],array[i]指向第一个2元素,满足条件所以被删除了,数组变为[0,1,2,4,5],第二个2的下标由3变为2
//在i=3的循环中,数组为[0,1,2,4,5],array[i]指向4,2被跳过了
//所以在使用 splice 的下一句,i = i - 1即可解决
let array = [0,1,2,2,4,5];
for(let i = 0;i<array.length;i++) {
    if (array[i] === 2) {
       array.splice(i, 1);
    }
}

具体可参考该博客:www.cnblogs.com/flywong/p/9…

执行结果:

执行用时:248 ms, 在所有 JavaScript 提交中击败了21.49%的用户

内存消耗:45.4 MB, 在所有 JavaScript 提交中击败了7.16%的用户

通过测试用例:74 / 74

up主的思路:

  1. 遍历数组,遇到首个不为0的元素,将其移动到第0个位置,再遇到第二个不为0的元素,将其移动到第1个位置,以此类推,直到数组内所有不为0的数都被移动,

  2. 再从index下标位置开始,将其及以后的元素都设置为0

绕的地方注意:

虽然index从0开始,在循环中一直都是不为0的元素下标,但最后一个不为0的元素被移动后,index++自增了1,于是此时index是该元素下标+1,所以此时若将index作为下标,该处位置应存放第一个0

var moveZeroes = function(nums) {
        let index = 0;
        for (let i = 0; i < nums.length; i++) {
            if (nums[i] !== 0) {
                nums[index] = nums[i];
                index++;
            }
        }
        for (let i = index; i < nums.length; i++) {
            nums[index] = 0;
            index++;
        }
};
执行结果:

执行用时:84 ms, 在所有 JavaScript 提交中击败了95.96%的用户

内存消耗:45.5 MB, 在所有 JavaScript 提交中击败了7.05%的用户

通过测试用例:74 / 74

力扣angela的思路:

快慢指针法

设置两个指针进入循环,快慢指针初始值都为0,都指向第一个元素,当快指针指向的元素不为0时,将其与慢指针指向的元素进行交换,然后慢指针自增,于是指针走向末尾时,所有不为0的元素都被依次交换到前面,后面都是0的元素。

var moveZeroes = function(nums) {
  let fast = 0,
    slow = 0;
  while (fast < nums.length) {
    if (nums[fast] !== 0) {
      [nums[fast], nums[slow]] = [nums[slow], nums[fast]];
      slow++;
    }
    fast++;
  }
  return nums;
};
执行结果:

执行用时:72 ms, 在所有 JavaScript 提交中击败了99.73%的用户

内存消耗:45.7 MB, 在所有 JavaScript 提交中击败了6.71%的用户

通过测试用例:74 / 74

3.LeetCode27题

题目描述

27. 移除元素

难度简单1172收藏分享切换为英文接收动态反馈

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

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

up主的思路:

双指针法,左右指针

左指针指向要移除的元素,右指针指向不要移除的元素。然后将其交换,使不要移除的元素移动到头部,要移除的元素移动到尾部。一直到左指针 >= 右指针时,结束循环。此时判断左指针指向的元素是不是要移除的元素,如果是,则返回长度为left,如果不是,则返回长度为left+1

var removeElement = function(nums, val) {
    let left = 0;
    let right = nums.length - 1;
    while (left < right) {
        while (left < right && nums[left] !== val) {
            left++;
        } 
        while (left < right && nums[right] === val) {
            right--;
        }
        [nums[left], nums[right]] = [nums[right], nums[left]];
    }
    return nums[left] === val ? left : left + 1;
};

力扣angela的思路:

双指针法,快慢指针

设置两个指针进入循环,快慢指针初始值都为0,都指向第一个元素,当快指针指向不要移除的元素时,将其赋值到慢指针,然后慢指针自增,于是指针走向末尾时,不要移除的元素都被依次移动到前面,slow的值就是不要移除的元素总数。虽然后面的元素仍然是不要移除的元素,但题目要求中不需要考虑数组中超出新长度后面的元素。

var removeElement = function(nums, val) {
  let fast = 0;
  let slow = 0;
  while (fast < nums.length) {
    if (nums[fast] !== val) {
      nums[slow] = nums[fast];
      slow++;
    }
    fast++;
  }
  return slow;
};