(1)算法 数组与字符串

209 阅读4分钟

探索部分 数组与字符串

数组

1 寻找数组的中心索引: 给定一个整数类型的数组 nums,请编写一个能够返回数组 “中心索引” 的方法。 我们是这样定义数组中心索引的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。如果数组不存在中心索引,那么我们应该返回-1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。

function sum (arr) {
  var nsum = 0;
  for (var i = 0; i < arr.length; i++) {
    nsum += arr[i]
  }
  return nsum;
}
var pivotIndex = function (nums) {
  if (nums instanceof Array && nums.length != 0) {
    if (sum(nums.slice(1)) == 0)
      return 0;
    for (var i = 1; i < nums.length - 1; i++) {
      var lsum = sum(nums.slice(0, i));
      var rsum = sum(nums.slice(i + 1));
      if (lsum == rsum)
        return i;
    }
    if (sum(nums.slice(0, nums.length - 1)) == 0)
      return nums.length - 1;
  }
  return -1;
}

2 搜索插入位置:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。

var searchInsert = function (nums, target) {
  var i = 0;
  if (nums instanceof Array && nums.length === 0) {
    return 0;
  }
  while (i < nums.length) {
    if (nums[i] >= target) {
      return i;
    }
    i++;
  }
  return nums.length
};

3 合并区间:给出一个区间的集合,请合并所有重叠的区间。

var merge = function (intervals) {
  if (intervals.length <= 1) {
    return intervals
  }
  // 先将数组按照区间最左边的大小顺序排序
  let arr = intervals.sort((a, b) => a[0] - b[0]);
  function unite (arr, i) {
    if (i == arr.length - 1) {
      return arr
    }
    // 如果下一个区间的左区间在本区间之间,则合并一次
    if (arr[i][0] <= arr[i + 1][0] && arr[i + 1][0] <= arr[i][1]) {
      arr[i] = [
         Math.min(arr[i][0], arr[i + 1][0]),
         Math.max(arr[i][1], arr[i + 1][1])
      ];
      // 合并之后删除冗余区间
      arr.splice(i + 1, 1);
    } else {

      // 如果没有合并,则找到下一个待合并区间
      i++;
    }
    return unite(arr, i)
  }
  return unite(arr, 0)
};

先对数组进行排序,再考虑合并的判断,用到递归的思想。

7.22

二维数组

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

var rotate = function (matrix) {
  let dd = []
  dd = matrix.map((val, matrixIndex) => {
    let arr = []
    for (let i = val.length - 1; i >= 0; i--) {
      // 第一行等于第一列,以此类推
      arr.push(matrix[i][matrixIndex])
    }
    return arr
  })
  console.log(dd)
  matrix.forEach((val, index) => {
    matrix[index] = dd[index]
  })
};

map方法会遍历第二个参数,返回结果拼接为一个数组;forEach方法会改变传入参数的值

5 零矩阵:编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。

var setZeroes = function (matrix) {
  let col = []
  for (let i = 0; i < matrix.length; i++) {
    let row = null
    for (let j = 0; j < matrix[i].length; j++) {
      if (matrix[i][j] == 0) {
        col.push(j);
        row = i;
      }
    }
    if (row != null) {
      for (let j = 0; j < matrix[i].length; j++) {
        matrix[i][j] = 0
      }
    }
  }
  console.log(col)
  for (let i = 0; i < matrix.length; i++) {
    for (let j of col) {
      matrix[i][j] = 0;
    }
  }
};

双重循环遍历

7.23

6 对角线遍历:给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素。

var findDiagonalOrder = function (matrix) {
     if (matrix.length < 1) {
    return []
  }
  var m = matrix.length;
  var n = matrix[0].length;
  var arr = []
  for (var x = 0; x < n; x++) {
    sum = x;
    var subarr = []
    for (var y = x; y > -1; y--) {
      subarr.push(matrix[sum - y][y])
      if (sum - y === m - 1) break;
    }
    if (x % 2 === 0) {
      subarr = subarr.reverse()
    }
    arr.push(subarr)
  }
  for (var y = 1; y < m; y++) {
    sum = y + n - 1;
    var subarr = [];
    for (var x = y; x < m; x++) {
      subarr.push(matrix[x][sum - x])
      if (x === m - 1|| sum - x === 0) break;
    }
    if ((n % 2 === 0 && y % 2 === 1) || (n % 2 === 1 && y % 2 === 0)) {
      subarr = subarr.reverse()
    }
    arr.push(subarr)
  }
  arr = arr.flat(Infinity)
  return arr
}

字符串

7 最长公共前缀:编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。

var longestCommonPrefix = function (strs) {
  var comstr = ''
  if (strs.length >= 1) {
    comstr = strs[0]
    for (var i = 1; i < strs.length; i++) {
      if (strs[i] === '')
        return ''
      for (var j = 0; j < strs[i].length; j++) {

        if (strs[i].length < comstr.length) {
          comstr = comstr.slice(0, strs[i].length) // 保证公共前缀长度最短
        }
        if (comstr[j] != strs[i][j]) {   //遇到不相等的字母,截掉后面的部分
          comstr = comstr.slice(0, j)
          break
        }
      }
    }
  }
  return comstr
};

8 翻转字符串里的单词:给定一个字符串,逐个翻转字符串中的每个单词。

var reverseWords = function (s) {
  if (s.length <= 1) {
    return s.trim()
  }
  var arr = s.trim().split(/\s+/)    //用正则表达式匹配一到多个空格 trim()去掉首尾空格
  var arr1 = []
  var arr2 = ''
  for (var i = arr.length - 1; i > -1; i--) {   //从后往前遍历,逐个添加到新的数组中
    arr1.push(arr[i])
  }
  arr2 = arr1.join(" ")  //join()将数组内的字符串用空格连接起来
  return arr2
};

双指针技巧

9 反转字符串:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

var reverseString = function (s) {
  let i = 0
  let j = s.length - 1   //双指针的方法 交换两个字符
  while (i < j) {
    var temp = s[j]
    s[j] = s[i]
    s[i] = temp
    i++
    j--
  }
  return s
}

10 数组拆分 I:给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。

var arrayPairSum = function (nums) {
  if (nums.length === 0) {
    return 0
  }
  nums = nums.sort((a, b) => a - b)   //给数组原地排序
  let sum = 0
  for (let i = 0; i < nums.length; i++) {  //所有下标为偶数的项的和即为所求
    if (i % 2 === 0)
      sum += nums[i]
  }
  return sum
};

11 两数之和 II - 输入有序数组:给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

var twoSum = function (numbers, target) {
  var map = new Map() //用到map对象
  let result = []
  for (let i = 0, len = numbers.length; i < len; i++) {
    if (map.has(numbers[i])) {  //如果找到了差值,就找到需要这个差值的那一项的值,即index1 当前下标加一是index2
      result.push(map.get(numbers[i]))
      result.push(i + 1)
    }
    map.set(target - numbers[i], i + 1) //把这一项的与目标的差作为键,下标加1作为值
  }
  return result
};

map.set(key,value) 设置map中的键值对
map.get(key) 返回键为key的value值
map.has(key),boolean值,判断map中是否有键为key的键值对

7.27

12 移除元素:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

var removeElement = function (nums, val) {
  if (nums.length < 1) {
    return 0
  }
  let j = 0;
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] != val) {
      nums[j] = nums[i];
      j++;
      console.log(j)
    }
  }
  return j;
};

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

var findMaxConsecutiveOnes = function (nums) {
  if (nums.length === 0) {
    return 0;
  }
  let max = 0;
  let j = 0;
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] === 1) {
      j++;
    }
    max = max > j ? max : j
    if (nums[i] != 1) {
      j = 0
    }
  }
  return max
};

14 长度最小的子数组:给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
(1)

var minSubArrayLen = function (s, nums) {
  if (nums.length < 1) {
    return 0;
  }
  let acc = 0;
  acc = nums.reduce((a, b) => { a + b }, 0)
  if (acc < nums) return 0

  let sublen = nums.length;
  for (let i = 0; i < nums.length; i++) {
    let count = 0;
    let sum = 0;
    for (j = i; j < nums.length; j++) {
      sum += nums[j];
      count++;
      if (sum >= s) {
        sublen = sublen < count ? sublen : count;
        break;
      }
    }
  }

  return sublen;

};

(2)

var minSubArrayLen = function (s, nums) {
  if (nums.length === 0) {
    return 0;
  }
  let acc = 0;
  let min = null;
  let slideWindow = [];
  for (let i = 0; i < nums.length + 1; i++) {
    const num = nums[i];
    while (acc >= s) {
      if (min === null || min > slideWindow.length) {
        min = slideWindow.length;
      }
      acc = acc - slideWindow.shift();
    }
    slideWindow.push(num);
    acc = slideWindow.reduce((a, b) => a + b, 0)
  }
  return min || 0
};

第二种方法是滑动窗口的方法,适合求连续字串的问题。reduce()方法可以用来求数组的和。

小结

14 杨辉三角:给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。

var generate = function (numRows) {
  let arr = [];
  if (numRows) {  //如果numsRows不等于0的时候,把第一行添加进去,否则就是空数组
    arr.push([1]);
  }
  for (let i = 1; i < numRows; i++) { //从第二行开始计算
    let item = [];
    let lastitem = arr[arr.length - 1]; //每次取上一个数组项
    for (let j = 0; j < lastitem.length - 1; j++) {
      item.push(lastitem[j] + lastitem[j + 1])  //计算和,添加到新的数组中
    }
    item.unshift(1);
    item.push(1);
    arr.push(item);
  }
  return arr
};

15 杨辉三角2:给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

var getRow = function (rowIndex) {
  let arr = [];
  arr.push([1]);
  for (let i = 1; i < rowIndex + 1; i++) {  //最后一次循环计算rowIndex行
    let item = [];
    let lastitem = arr[arr.length - 1]; //每次取上一个数组项
    for (let j = 0; j < lastitem.length - 1; j++) {
      item.push(lastitem[j] + lastitem[j + 1])  //计算和,添加到新的数组中
    }
    item.unshift(1);
    item.push(1);
    arr.push(item);
  }
  return arr[rowIndex] //返回rowIndex行
};

优化到O(k)空间

var getRow = function (rowIndex) {
  let item = [1];
  for (let i = 0; i < rowIndex; i++) {  //最后一次循环计算rowIndex行
    for (let j = 0; j < item.length - 1; j++) {
      item[j] = item[j] + item[j + 1]
    }
    item.unshift(1)
  }

  return item //返回行
};

16 反转字符串中的单词 III:给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

var reverseWords = function (s) {
  let arr = s.split(/\s+/);  //先将字符串按单词分割成数组
  for (let i = 0; i < arr.length; i++) { 
    let arr2 = arr[i].split("")  //将每个单词分割成数组
    arr2.reverse()           //翻转
    arr[i] = arr2.join("") //再拼接成一个单词
  }
  s = arr.join(" ")
  return s
};

17 寻找旋转排序数组中的最小值:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。请找出其中最小的元素。你可以假设数组中不存在重复元素。

var findMin = function (nums) {
  if (nums.length === 0) {
    return []
  }
  for (let i = 0; i < nums.length - 1; i++) {
    if (nums[i] > nums[i + 1]) {  //如果后一个比当前值小,就找到了
      return nums[i + 1]
    }
  }
  return nums[0]
};

18 删除排序数组中的重复项:给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

var removeDuplicates = function (nums) {
  let arr = nums.reduce((init, current) => {
    if (init.length === 0 || init[init.length - 1] != current) {
      init.push(current)
    }
    return init
  }, [])
  arr.forEach((val, index) => {
    nums[index] = arr[index];
  });
  return arr.length
};

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

var moveZeroes = function (nums) {
  //快慢指针
  let count = 0
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] != 0) {
      if (count != i) {
        nums[count] = nums[i];
        nums[i] = 0;
      }
      count++;
    }
  }
};

20 :最长回文子串(动态规划)