数组和字符串

449 阅读5分钟

数组是数据结构的基本模块之一,因为字符串是由字符数组形成的,所以二者是相似的。

数组简介

  • 集合、列表、数组,三者的区别

    • 集合的定义: 一个或多个确定元素所构成的整体, 比如商店的东西,或桌面上的物品

    • 集合的特点:1 集合里的元素类型不一定相同 2 元素没有顺序

    • 列表(又称线性列表)的定义:是一种由数据项构成的有限序列,按照一定的线性顺序,组成一个数据项的集合

    • 列表的特点:1 有顺序 2 长度可变

    • 列表可看成是一个购物清单, 1 条目类型可能不同 2 但按照顺序排序 3 长度可变,可往清单里增加、删除条目

    • 列表最常见的表现形式有数组和链表,特殊的表现形式有栈和队列

    • 数组是列表的表现形式之一,在不同的编程语言有一定的差别,比如c++和Java中数组的元素类型必须保持一致,Python中则可以不同,Python中的数组叫做List,有更多高级功能

    • 如何区别列表和数组? --- 有无索引

    • 数组中的元素是在内存中是连续存储的(javaScript则不连续哈希映射),且每个元素所占内存大小相同

  • 数组的操作都有哪些

    • 读取元素
      • 通过索引来读取,一般从0开始,时间复杂度是常数,O(1)

    • 查找元素
      • 查找数组是否包含某个元素,从数组从开头往后查看,最快的情况数组不包含这个元素,需查n次,n为数组的长度,时间复杂度为O(n)

    • 插入元素
      • 若在末尾插入,只需通过数组的长度和位置计算出需要插入的内存地址,然后插入到指定位置即可

      • 若在中间插入,则部分靠后的元素需要逐个往后排列,为腾出空间,再插入

    • 删除元素
      • 删除和插入类似,删除元素会留下一段空缺,需要后面的元素进行依次填补操作

  • 寻找数组的中心索引

    • 给你一个整数数组 nums,请编写一个能够返回数组 “中心下标” 的方法。

      数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

      如果数组不存在中心下标,返回 -1 。如果数组有多个中心下标,应该返回最靠近左边的那一个。

      注意:中心下标可能出现在数组的两端。

    /**
     * @param {number[]} nums
     * @return {number}
     */
    var pivotIndex = function(nums) {
        function sum(items) {
            if (items.length === 0) {
                return 0;
            }
            return items.reduce((a, b)=> {
                return a + b;
            })
        };
        var center = -1;
        for(let i = 0; i < nums.length;i++) {
            if (sum(nums.slice(0,i)) === sum(nums.slice(i+1))) {
                center = i;
                break;
            }
        }
        return center;
    };
  • 搜索插入位置

    • 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

      你可以假设数组中无重复元素。

      示例 1:

      输入: [1,3,5,6], 5 输出: 2 示例 2:

      输入: [1,3,5,6], 2 输出: 1

    /**
     * @param {number[]} nums
     * @param {number} target
     * @return {number}
     */
    var searchInsert = function(nums, target) {
        if (nums.indexOf(target) !== -1) {
            return nums.indexOf(target)
        }
        const index = nums.findIndex(i => i >= target);
        if (index !== -1) {
            return index > 0 ? index : 0;
        }
        return nums.length;
    };
  • 合并区间

    • 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。 

      以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

      示例 1:

      输入:intervals = [[1,3],[2,6],[8,10],[15,18]] 输出:[[1,6],[8,10],[15,18]] 解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. 示例 2:

      输入:intervals = [[1,4],[4,5]] 输出:[[1,5]] 解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

    /**
     * @param {number[][]} intervals
     * @return {number[][]}
     */
    var merge = function(intervals) {
        intervals.sort((a, b) => a[0] - b[0]);
        const items = [intervals[0]];
        for (let i = 1; i < intervals.length;i++) {
            if (intervals[i][1] <= items[items.length - 1][1]) {
                continue;
            }
            if (intervals[i][0] <= items[items.length - 1][1]) {
                items[items.length - 1][1] = intervals[i][1];
            } else {
                items.push(intervals[i]);
            }
        }
        return items
        }
    }

二维数组

  • 二维数组简介

    • 一种结构比较特殊的数组,只是将每个元素变成了一维数组
    • 本质上还是一维数组,从索引0开始,可看做是一个矩阵
  • 旋转矩阵

    • 给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。不占用额外内存空间能否做到?

    const matrix = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
    ];

    matrix.map((n, i) => {

        matrix.push([]);
        let m = n.length;

        while (m > 0) {
            m--;
            matrix[n.length + i].push(matrix[m][i]);
        };
    });
    matrix.splice(0, matrix.length / 2);
  • 零矩阵

    • 编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。
    输入:
    [
      [1,1,1],
      [1,0,1],
      [1,1,1]
    ]
    输出:
    [
      [1,0,1],
      [0,0,0],
      [1,0,1]
    ]
    var setZeroes = function(matrix) {
        const indexes = [[], []];
        matrix.map((n, m) => {
            n.map((i, j) => {
                if (i === 0) {
                    if (!indexes[0].includes(m)) {
                        indexes[0].push(m);
                    }
                    if (!indexes[1].includes(j)) {
                        indexes[1].push(j);
                    }
                }
            })
        })
        indexes[0].map(n => {
            matrix[n].fill(0);
        });
        indexes[1].map(n => {
            matrix.map((m ,i) => {
                matrix[i][n] = 0;
            })
        })
    };

字符串

  • 最长公共前缀

    • 编写一个函数来查找字符串数组中的最长公共前缀,如果不存在公共前缀,返回空字符串 ""
    输入:strs = ["flower","flow","flight”]
    输出:”fl”
    输入:strs = ["dog","racecar","car”]
    输出:""
    解释:输入不存在公共前缀。
    var longestCommonPrefix = function(strs) {
        let object = [], maxLength = 0;
        strs.map((s, t) => {
            maxLength = Math.max(s.length, maxLength);
        })
        for (let i = 0; i < maxLength; i++) {
            strs.map(n => {
                if (object[i]) {
                    object[i].push(n[i] || '');
                } else {
                    object[i] = [n[i] || ''];
                }
            })
        };
        let str = [];
        for (let index = 0; index < object.length;index++) {
            if (index !== str.length) {
                break;
            }
            if (object[index].every(m => m === object[index][0])) {
                str.push(object[index][0]);
            }
        }
        object = null;
        maxLength = null;
        return str.join('');
  };
  • 翻转字符串里的单词

    • 给你一个字符串 s ,逐个翻转字符串中的所有 单词 , 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开, 请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。

      分以下几步:

      1. 先取出左右两边的空格,防止影响

      2. 根据多个空格切割成数组

      3. 利用数组的反转,倒叙

      4. 切割成字符串

    var reverseWords = function(s) {

        s = s.replace(/(^\s+)|(\s+$)/g, '');

        return s.split(/\s+/).reverse().join(' ');

    };
  • 实现strStr

    输入: haystack = "hello", needle = "ll"
    输出: 2
    
    输入: haystack = "aaaaa", needle = "bba"
    输出: -1
    
    // 第一种方法
    var strStr = function(haystack, needle) {

        let s = -1;

        if (!needle) {

            return 0;

        }
        for (let i = 0; i < haystack.length; i++) {

            if (haystack[i] === needle[0]) {

            if (haystack.slice(i, i + needle.length) === needle) {

                s = i;    

                break;
             }
            }
         }
        return s;
    };

    // 第二种方法
    var strStr = function(haystack, needle) {
        return haystack.indexOf(needle)
    }

双指针技巧

  • 数组拆分

    • 给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大,返回该最大总和 。
    输入:nums = [1,4,3,2]
    输出:4
    解释:所有可能的分法(忽略元素顺序)为:
    
    1. (1, 4), (2, 3) -> min(1, 4) + min(2, 3) = 1 + 2 = 3
    2. (1, 3), (2, 4) -> min(1, 3) + min(2, 4) = 1 + 2 = 3
    3. (1, 2), (3, 4) -> min(1, 2) + min(3, 4) = 1 + 3 = 4

    所以最大总和为 4

    输入:nums = [6,2,6,5,1,2]
    输出:9

    解释:最优的分法为 (2, 1), (2, 5), (6, 6). min(2, 1) + min(2, 5) + min(6, 6) = 1 + 2 + 6 = 9
    
    var arrayPairSum = function(nums) {
    nums.sort((a, b) => a - b);
    let i = 0, j = 1, n = 0;
    while(j < nums.length) {
        n += Math.min(nums[i], nums[j]);
        i += 2;
        j += 2;
    }
    return n;
};