• 作者:老汪软件技巧
  • 发表时间:2024-09-28 17:02
  • 浏览量:

每日的知识积累,包括 五个 Leetcode 算法题,十个前端八股文题,四个英语表达积累。从今天开始倒计时 100 天,到时候换个工作。

1. 五道算法题第一题: 29. 两数相除 /problems/di…

给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。
整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。
返回被除数 dividend 除以除数 divisor 得到的 商 。
注意:假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−231,  231 − 1] 。本题中,如果商 严格大于 231 − 1 ,则返回 231 − 1 ;如果商 严格小于 -231 ,则返回 -231 。
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = 3.33333.. ,向零截断后得到 3 。
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = -2.33333.. ,向零截断后得到 -2 。
提示:
-231 <= dividend, divisor <= 231 - 1
divisor != 0

var divide = function (dividend, divisor) {
  if (dividend == 0) {
    return 0;
  }
  if (dividend === -(2 ** 31) && divisor === -1) {
    return 2 ** 31 - 1;
  }
  if (dividend === -(2 ** 31) && divisor === 1) {
    return -(2 ** 31);
  }
  let negative = (dividend ^ divisor) < 0; // 获取最终结果的正负性
  let t = Math.abs(dividend);
  let d = Math.abs(divisor);
  let result = 0;
  for (let i = 31; i >= 0; i--) {
    if (t >>> i >= d) {
      // 这里不能是 >> 不然t将变为负值
      result += 1 << i;
      t -= d << i;
    }
  }
  return negative ? -result : result; // 添加符号不需要那么复杂,只需要在前面添加一个符号即可
};

实话实说,没做出来,技巧性太强了,上面贴的是标准答案。得分情况:66.51% 11.17%总结提升let negative = (dividend ^ divisor) < 0; 可以快速获取结果的正负性。>>> 和 >> 的区别,在于前者不会把数变成负数。return negative ? -result : result; 添加符号不需要那么复杂,只需要在前面添加一个符号即可我的答案

/**
 * @param {number} dividend
 * @param {number} divisor
 * @return {number}
 */
var divide = function (dividend, divisor) {
  let prefix = 1;
  if ((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) {
    prefix = -1;
  }
  dividend = Math.abs(dividend);
  divisor = Math.abs(divisor);
  if (dividend === divisor) {
    return prefix === 1 ? 1 : -1;
  }
  if (divisor === 1) {
    return prefix === 1
      ? Math.min(dividend, 2 ** 31 - 1)
      : Math.max((-2) ** 31, -dividend);
  }
  if (divisor > dividend) return 0;
  const tmp = [divisor, divisor + divisor];
  const counts = [1, 2];
  while (true) {
    const _a = counts[1];
    const _b = tmp[1];
    if (dividend > _b) {
      tmp[0] = _b;
      tmp[1] = _b + _b;
      counts[0] = _a;
      counts[1] = _a + _a;
    } else {
      break;
    }
  }
  while (true) {
    const mid = (tmp[0] >>> 1) + (tmp[1] >>> 1);
    const midCount = (counts[0] >>> 1) + (counts[1] >>> 1);
    if (dividend >= mid) {
      tmp[0] = mid;
      counts[0] = midCount;
    } else {
      tmp[1] = mid;
      counts[1] = midCount;
    }
    // console.log('11', counts, tmp, dividend)
    if (tmp[1] - tmp[0] <= divisor) break;
  }
  let rawResult = counts[0];
  if (Math.abs(dividend) - tmp[0] === divisor) {
    rawResult = counts[1];
  }
  return prefix === 1
    ? Math.min(rawResult, 2 ** 31 - 1)
    : Math.max((-2) ** 31, -rawResult);
};

25.58% 77.68% 有些技法缺少了是做不出来的。上面的代码实现了不使用除法的二分法。

第二题: 507 完美数 /problems/pe…

对于一个 正整数,如果它和除了它自身以外的所有 正因子 之和相等,我们称它为 「完美数」。
给定一个 整数 n, 如果是完美数,返回 true;否则返回 false。
示例 1:
输入:num = 28
输出:true
解释:28 = 1 + 2 + 4 + 7 + 14
1, 2, 4, 7, 和 14 是 28 的所有正因子。
示例 2:
输入:num = 7
输出:false
提示:
1 <= num <= 108

/**
 * @param {number} num
 * @return {boolean}
 */
var checkPerfectNumber = function (num) {
  if (num <= 2) return false;
  const upperlimit = ~~Math.sqrt(num);
  let rst = 0;
  for (let i = 1; i <= upperlimit; i++) {
    if (num % i === 0) {
      rst += i + num / i;
    }
  }
  return rst === num + num;
};

得分情况:73.37% 68.47%第三题: 50 Power(x,n) /problems/po…

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。
示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:
输入:x = 2.10000, n = 3
输出:9.26100
示例 3:
输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25
提示:
-100.0 < x < 100.0
-231 <= n <= 231-1
n 是一个整数
要么 x 不为零,要么 n > 0 。
-104 <= xn <= 104

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function (x, n) {
  if (n == 0 || n == 1) {
    return n == 0 ? 1 : x;
  } else if (n < 0) {
    return myPow(1 / x, Math.abs(n));
  } else {
    return n % 2 == 0
      ? myPow(x * x, n / 2)
      : myPow(x * x, Math.floor(n / 2)) * x;
  }
};

得分情况:48.12% 13.87% 没做出来,尴尬总结提升不必离散数列因为函数本身就可以递归调用对于负数的情况,我们可以抽离提取附加值然后将其变成正数,算出结果之后再将附加值附加作用考虑上即可。第四题: 372 超级次方 /problems/su…

你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出。
示例 1:
输入:a = 2, b = [3]
输出:8
示例 2:
输入:a = 2, b = [1,0]
输出:1024
示例 3:
输入:a = 1, b = [4,3,3,8,5,2]
输出:1
示例 4:
输入:a = 2147483647, b = [2,0,0]
输出:1198
提示:
1 <= a <= 231 - 1
1 <= b.length <= 2000
0 <= b[i] <= 9
b 不含前导 0

/**
 * @param {number} a
 * @param {number[]} b
 * @return {number}
 */
var superPow = function (a, b) {
  /**
   * @param {number} a
   * @param {number[]} b
   * @return {number}
   */
  var superPow = function (a, b) {
    a = BigInt(a);
    a = a % 1337n;
    function innerSuperPow(a, b) {
      // console.log('a, b:', a, b)
      // console.log('b:', b)
      if (a === 1n || b === 0n) return 1n;
      if (a === 0n) return 0n;
      if (b > 10n) {
        const _a = b % 10n;
        const _b = (b - _a) / 10n;
        const _c = innerSuperPow(a, _b) % 1337n;
        // console.log(_a, _b, _c)
        return (BigInt(innerSuperPow(_c, 10n)) * innerSuperPow(a, _a)) % 1337n;
      }
      const _rst = a ** b % 1337n;
      // console.log('_rst:', _rst)
      return _rst;
    }
    function innerSuperPow2(x, y, z) {}
    let rst = 1n;
    const len = BigInt(b.length);
    b.forEach((c, i) => {
      rst =
        (rst * innerSuperPow(a, BigInt(c) * 10n ** (len - BigInt(i) - 1n))) %
        1337n;
    });
    return Number(rst);
  };
  console.log(superPow(2147483647, [2, 0, 0]));
};

以上代码超时了!后面再改进吧!

得分情况:--第五题:682 棒球比赛 /problems/ba…

你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:
整数 x - 表示本回合新获得分数 x
"+" - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
"D" - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
"C" - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
请你返回记录中所有得分的总和。
示例 1:
输入:ops = ["5","2","C","D","+"]
输出:30
解释:
"5" - 记录加 5 ,记录现在是 [5]
"2" - 记录加 2 ,记录现在是 [5, 2]
"C" - 使前一次得分的记录无效并将其移除,记录现在是 [5].
"D" - 记录加 2 * 5 = 10 ,记录现在是 [5, 10].
"+" - 记录加 5 + 10 = 15 ,记录现在是 [5, 10, 15].
所有得分的总和 5 + 10 + 15 = 30
示例 2:
输入:ops = ["5","-2","4","C","D","9","+","+"]
输出:27
解释:
"5" - 记录加 5 ,记录现在是 [5]
"-2" - 记录加 -2 ,记录现在是 [5, -2]
"4" - 记录加 4 ,记录现在是 [5, -2, 4]
"C" - 使前一次得分的记录无效并将其移除,记录现在是 [5, -2]
"D" - 记录加 2 * -2 = -4 ,记录现在是 [5, -2, -4]
"9" - 记录加 9 ,记录现在是 [5, -2, -4, 9]
"+" - 记录加 -4 + 9 = 5 ,记录现在是 [5, -2, -4, 9, 5]
"+" - 记录加 9 + 5 = 14 ,记录现在是 [5, -2, -4, 9, 5, 14]
所有得分的总和 5 + -2 + -4 + 9 + 5 + 14 = 27
示例 3:
输入:ops = ["1"]
输出:1
提示:
1 <= ops.length <= 1000
ops[i] 为 "C"、"D"、"+",或者一个表示整数的字符串。整数范围是 [-3 * 104, 3 * 104]
对于 "+" 操作,题目数据保证记录此操作时前面总是存在两个有效的分数
对于 "C" 和 "D" 操作,题目数据保证记录此操作时前面总是存在一个有效的分数

/**
 * @param {string[]} operations
 * @return {number}
 */
var calPoints = function (operations) {
  const _tmp = [];
  for (let i = 0; i < operations.length; i++) {
    const cur = operations[i];
    switch (cur) {
      case "+":
        _tmp.push(_tmp[_tmp.length - 1] + _tmp[_tmp.length - 2]);
        break;
      case "D":
        _tmp.push(_tmp[_tmp.length - 1] * 2);
        break;
      case "C":
        _tmp.pop();
        break;
      default:
        _tmp.push(parseFloat(cur));
        break;
    }
  }
  // console.log(_tmp);
  return _tmp.reduce((a, i) => a + i, 0);
};

得分情况:60.53% 83.53%2. 十道面试题2.1 vue 的生命周期有哪些及每个生命周期做了什么?

beforeCreate 是 new Vue()之后触发的第一个钩子,在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问。

created 在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发 updated 函数。可以做一些初始数据的获取,在当前阶段无法与 Dom 进行交互,如果非要想,可以通过 vm.$nextTick 来访问 Dom。

beforeMount 发生在挂载之前,在这之前 template 模板已导入渲染函数编译。而当前阶段虚拟 Dom 已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发 updated。

mounted 在挂载完成后发生,在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom 节点,使用$refs 属性对 Dom 进行操作。

beforeUpdate 发生在更新之前,也就是响应式数据发生更新,虚拟 dom 重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。

updated 发生在更新完成之后,当前阶段组件 Dom 已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。

beforeDestroy 发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。

destroyed 发生在实例销毁之后,这个时候只剩下了 dom 空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。

2.2 vue 响应式原理是什么?vue3 的响应式有何不同

❝Proxy 只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的呢?❞

判断当前 Reflect.get 的返回值是否为 Object,如果是则再通过 reactive 方法做代理, 这样就实现了深度观测。

❝监测数组的时候可能触发多次 get/set,那么如何防止触发多次呢?❞

我们可以判断 key 是否为当前被代理对象 target 自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行 trigger。

2.3 Vue2.x 和 Vue3.x 的区别2.4 谈一谈对 MVVM 的理解

MVVM 是 Model-View-ViewModel 缩写,也就是把 MVC 中的 Controller 演变成 ViewModel。Model 层代表数据模型,View 代表 UI 组件,ViewModel 是 View 和 Model 层的桥梁,数据会绑定到 viewModel 层并自动将数据渲染到页面中,视图变化的时候会通知 viewModel 层更新数据。

2.5 在 vue2.x 中如何检测数组的变化

使用了函数劫持的方式,重写了数组的方法,Vue 将 data 中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组 api 时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。

2.6 V-model 双向绑定的原理是什么

v-model 本质就是一个语法糖,可以看成是 value + input 方法的语法糖。 可以通过 model 属性的 prop 和 event 属性来进行自定义。原生的 v-model,会根据标签的不同生成不同的事件和属性。

2.7 分别说下两个版本的 diff 算法的不同

简单来说,diff 算法有以下过程

正常 Diff 两个树的时间复杂度是 O(n^3),但实际情况下我们很少会进行跨层级的移动 DOM,所以 Vue 将 Diff 进行了优化,从 O(n^3) -> O(n),只有当新旧 children 都为多个子节点时才需要用核心的 Diff 算法进行同层级比较。

Vue2 的核心 Diff 算法采用了双端比较的算法,同时从新旧 children 的两端开始进行比较,借助 key 值找到可复用的节点,再进行相关操作。相比 React 的 Diff 算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。

Vue3.x 借鉴了 ivi 算法和 inferno 算法

在创建 VNode 时就确定其类型,以及在 mount/patch 的过程中采用位运算来判断一个 VNode 的类型,在这个基础之上再配合核心的 Diff 算法,使得性能上较 Vue2.x 有了提升。该算法中还运用了动态规划的思想求解最长递归子序列。

2.8 vue 组件通信方式及原理父子组件通信

兄弟组件通信javascript 代码解读复制代码 Event Bus实现跨组件通信Vue.prototype.$bus = new VueVuex

跨级组件通信

2.9 说一下 hash 路由和 history 路由2.11 v-if 和 v-show 的区别

当条件不成立时,v-if 不会渲染 DOM 元素,v-show 操作的是样式(display),切换当前 DOM 的显示和隐藏。

3. 五句英语积累where is she from?where is the bathroom?eleven fifty two -- $11.52at 3 o'clockdo you have anything cheaper?do you take credit cards?