前端算法面试必刷题系列[2]

1,017 阅读5分钟

这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。

4. 整数反转 (Reverse Integer)

标签

  • String
  • 简单

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

  1. 给你一个 32 位的有符号整数 x ,返回 x 中每位上的数字反转后的结果。

  2. 如果反转后整数超过 32 位的有符号整数的范围 [−2^31,  2^31 − 1] ,就返回 0。

基本知识

  • Math.abs(x) 函数返回指定数字 “x“ 的绝对值。
  • Math.pow() 函数返回基数(base)的指数(exponent)次幂,即 base^exponent。
  • String.prototype.split() 方法使用指定的分隔符字符串将一个String对象分割成子字符串数组,以一个指定的分割字串来决定每个拆分的位置。
    • str.split([separator[, limit]])
    • separator指定表示每个拆分应发生的点的字符串。separator 可以是一个字符串或正则表达式。 如果纯文本分隔符包含多个字符,则必须找到整个字符串来表示分割点。如果在str中省略或不出现分隔符,则返回的数组包含一个由整个字符串组成的元素。如果分隔符为空字符串,则将str原字符串中每个字符的数组形式返回。
    • limit 一个整数,限定返回的分割片段数量。当提供此参数时,split 方法会在指定分隔符的每次出现时分割该字符串,但在限制条目已放入数组时停止。如果在达到指定限制之前达到字符串的末尾,它可能仍然包含少于限制的条目。新数组中不返回剩下的文本。

基本思路

  1. 利用字符串转数组split,翻转reverse,再合并 join(''),来变成一个翻转的字符串
  2. 利用 * 1(-1) 操作隐式类型转换成整形
  3. 最后根据限制条件输出就行了

写法实现

/**
 * @param {number} x
 * @return {number}
 */
var reverse = function(x) {
  // 利用字符串转数组,翻转,再合并 join(''),来变成一个翻转的字符串
  let temp = `${Math.abs(x)}`.split('').reverse().join('')
  // 利用 * 1(-1) 操作隐式类型转换成整形
  let res = x > 0 ? temp * 1 : temp * -1
  // 最后根据限制条件输出就行了
  if (res > Math.pow(2, 31) - 1 || res < Math.pow(-2, 31)) {
    return 0
  } else {
    return res
  }
}

另外还有一种常用写法

var reverse = function(x) {
  let temp = 0
  while (x !== 0) {
    temp = temp * 10 + x % 10
    // 位运算取整
    x = ~~(x / 10)
  }
  return temp
}

这种写法主要是考取整方式

js取整总结

大致分两类

直接取整(不考虑小数点后的部分)

  1. parseInt(number)
    • 这大概是取整最常用的方法了,因为parseInt()不是只能处理Number类型,还可以处理字符串类型的。
    • parseInt()处理在处理字符串时,会从第一个不是空格的字符开始处理。如果第一个不是数字字符或者负号,则返回NaN;如果是数字字符,则会一直处理到不是数字字符为止。
    • 注意,parseInt()可以识别各种整数格式(十进制,八进制和十六进制)。
      var num1 = parseInt("2015nov"),  //2015
      num2 = parseInt(""),  //NaN
      num3 = parseInt("0xA"),  //10(十六进制)
      num4 = parseInt(20.15),  //20
      num5 = parseInt(-20.15),  //-20
      num6 = parseInt("070");  //56(八进制数)
    
  2. ~~number , number^0 , number<<0
    • 位运算,所有取整之中最快的
      var num1 = ~~20.15,  //20
      num2 = ~~(-20.15),  //-20
      num3 = 20.15^0,  //20
      num4 = (-20.15)^0,  //-20
      num5 = 20.15 << 0,  //20
      num6 = (-20.15) << 0  //-20
    

计算后取整(例如四舍五入,向上取整,向下取整)。

  1. 四舍五入 Math.round(number) Math.round() 将数值四舍五入为最接近的整数。现实中很少有用到负数的四舍五入的,但是我们也可以看几个负数的例子。
      var num1 = Math.round(20.1),  //20
      num2 = Math.round(20.5),  //21
      num3 = Math.round(20.9),  //21
      num4 = Math.round(-20.1),  //-20
      num5 = Math.round(-20.5),  //-20 注意这里是-20而不是-21
      num6 = Math.round(-20.9);  //-21
    
  2. 向上取整 Math.ceil(number) 取向上最接近的整数。进一法
      var num1 = Math.ceil(20.1),  //21
      num2 = Math.ceil(20.6),  //21
      num4 = Math.ceil(-20.1),  //-20
      num5 = Math.ceil(-20.7),  //-20
    
  3. 向下取整 Math.floor(number) 取向下最接近的整数。退一法(去尾法)
      var num1 = Math.floor(20.1),  //20
      num2 = Math.floor(20.6),  //20
      num4 = Math.floor(-20.1),  //-21
      num5 = Math.floor(-20.7),  //-21
    

进一法是去掉多余部分的数字后,在保留部分的最后一个数字上加1。 这样得到的近似值为过剩近似值(即比准确值大)。 用进一法凑整时,凑整到哪一位,无论这位后面一位上的数是几,只要后面的数字不全是0,都向这一位进一,然后再把这一位后面的数都改写成0。

5. 回文数 (palindrome-number)

标签

  • String
  • 简单

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

判断一个整数是否是回文数

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

基本知识

  • 跟上面差不多,字符串操作

基本思路

太简单,直接看代码

写法实现

/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function(x) {
    return `${x}`.split('').reverse().join('') === `${x}`
};

6. 盛最多水的容器 (container-with-most-water)

标签

  • 双指针
  • 中等

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

给出一个非负整数数组 a1,a2,a3,…… an,每个整数标识一个竖立在坐标轴 x 位置的一堵高度为 ai 的墙,选择两堵墙,和 x 轴构成的容器可以容纳最多的水。

基本知识

对撞指针的思路。

首尾分别 2 个指针,每次移动以后都分别判断长宽的乘积是否最大

基本思路

  1. 在初始时,左右指针分别指向数组的左右两端, 容纳的水量是由 两个指针指向的数字中较小值 x 指针之间的距离
  2. 每次比较面积之后,我们移动数字较小的那个指针,直到左右指针指向一处,中途得到的最大值就是结果。

写法实现

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
  // 解构赋值声明方式
  let [maxNow, start, end] = [0, 0, height.length - 1]
  while (start < end) {
    // 设置宽高 宽度是(结束位置 - 开始位置) 高度是 长/宽 中小的那个
    let width = end - start
    let high = 0
    // 选取后指针移动
    if (height[start] < height[end]) {
      high = height[start]
      start++
    } else {
      high = height[end]
      end--
    }
    if (maxNow < high * width) {
      maxNow = high * width 
    }
  }
  return maxNow
};

console.log(maxArea([1,8,6,2,5,4,8,3,7]))

双指针正确性验证

双指针代表了什么?

双指针代表的是可以作为容器边界的所有位置的范围。在一开始,双指针指向数组的左右边界,表示数组中所有的位置都可以作为容器的边界,因为我们还没有进行过任何尝试。在这之后,我们每次将对应的数字较小的那个指针往另一个指针的方向移动一个位置,就表示我们认为这个指针不可能再作为容器的边界了

那么为什么对应的数字较小的那个指针不可能再作为容器的边界了?

考虑第一步,假设当前左指针和右指针指向的数分别为 xy,不失一般性,我们假设 x≤y。同时,两个指针之间的距离t。那么,它们组成的容器的容量为:min(x, y) * t = x * t

我们可以断定,如果我们保持左指针的位置不变,那么无论右指针在哪里,这个容器的容量都不会超过 x * t 了。因为取决于短板 x,宽减少,面积必然变小。也就是我们任意向左移动右指针,指向的数为y1,两个指针之间的距离为 t1,那么显然有 t1 < t 并且 min(x,y1) ≤ min(x,y)

即无论我们怎么移动右指针,得到的容器的容量都小于移动前容器的容量。也就是说,这个左指针对应的数不会作为容器的边界了,那么我们就可以丢弃这个位置,将左指针向右移动一个位置,此时新的左指针于原先的右指针之间的左右位置,才可能会作为容器的边界。

这样以来,我们将问题的规模减小了,被我们丢弃的那个位置就相当于消失了。此时的左右指针,就指向了一个新的、规模减少了的问题的数组的左右边界,因此,我们可以继续像之前(考虑第一步)那样考虑这个问题。

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the reiver monster,我看到就通过,暗号对不上不加哈

参考