前端算法必刷题系列[59]

158 阅读6分钟

这是我参与更文挑战的第 7 天,活动详情查看 更文挑战

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

112. Pow(x, n) (powx-n)

标签

  • 分治
  • 中等

题目

leetcode 传送门

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

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,x^n)。

示例 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

基本思路

当然我们这不考虑投机取巧的办法,比如调库函数或者暴力直接乘乘乘。

我们思考效率高的做法,用分治

分治法:“分而治之” —— 就是把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并

其实我们真的需要深刻理解并运用这种算法,并认真思考,分治,它除了是个算法,能不能是我们的面对需要完成复杂目标时的一个通用的思想武器

讲个从某个地方看到的小故事,具体真实不考量,看个大概思想就成

  • 埃隆·马斯克创建了一个太空探索公司 SpaceX。SpaceX 有一个目标是,送100万人上火星(复杂问题)。

  • 美国政府曾经算过一笔账,把一个人送上火星,以现有技术是可实现的,需要花大概100亿美金

  • 如果照此计算,实现马斯克的目标,送100万人上火星就要1万万亿。这是什么概念呢?这笔钱相当于美国 500 年的GDP,实在太贵了,贵到连美国政府都无法负担。

  • 马斯克怎么解决这个问题呢?首先他的目标变了,他准备把人均费用降到50万美元,也就是一个想移民的人,把地球房子卖了能够凑出的钱。原来需要100亿美金,现在要降到50万美金,需要降低2万倍

  • 当然,降低2万倍依然是一个听起来很遥远的目标。所以,我们关注的重点来了:马斯克的第二步是,把2万分解20×10×100。这是一道简单的数学题,也是马斯克三个重点的努力方向。(分治思想)

  • 先看“20”:现在的火星飞船一次只能承载5个人,马斯克的打算是,把火箭造大一点,一次坐100人,这样,就等于把成本降低20倍。如果你关注新闻的话,会发现 SpaceX 确实在进行这方面的尝试,

  • 再来看“10”:马斯克认为自己是私营公司,效率高,成本可以降到十分之一。他们也正在向这个方向努力,SpaceX 的成本目前已经降到了同行的五分之一。

  • 最后的“100”是什么呢?就是回收可重复使用的火箭。如果这个目标能实现,发射火箭的成本就只是燃料成本了。这也就是我们频频看到的 SpaceX 试飞火箭新闻的原因。

这么算下来,你是不是觉得,马斯克的目标不像最开始听到的那样不靠谱了呢?正是通过将宏大目标进行任务分解,马斯克才能将一个看似不着边际的目标向前推进。

一个大问题,我们都很难给出答案,但回答小问题却是我们擅长的。我们要把任务分解到我们可无脑执行的地步能轻松落地,就行了。

其实我们就可以把这个问题化简成更简单的子问题

// n 是 偶数 4
x * x * x * x
      |
x * x | x * x   
其实就是两个 n = 2 (n / 2)的解的乘积 
// n 是奇数的话 比如 n = 5
x * x * x * x * x
把一个 x 提出,就变成偶数了,再乘偶数分治计算结果
x * 两个 (n / 2) 的解的乘积 

另外要注意 n < 0 的情况, 看代码

写法实现

var myPow = function(x, n) {
  if (n === 0) {
    // 任何数的 0 次方都是 1
    return 1
  } else if (n < 0) {
    // 幂次小于0,用倒数
    return 1 / myPow(x, -n)
  } else if (n % 2 !== 0) {
    // 如果 n 是奇数
    return x * myPow(x, n - 1)
  } else {
    // 如果 n 是偶数,分成2份递归
    return myPow(x * x, n / 2)
  }
};


console.log(myPow(2, 10)) // 1024
console.log(myPow(2, -2)) // 0.25

此算法时间复杂度为 O(logn)

113. 多数元素 (majority-element)

标签

  • 分治
  • 简单

题目

leetcode 传送门

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

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

尝试设计时间复杂度为 O(n)空间复杂度为 O(1) 的算法解决此问题。

示例 1:

输入:[3,2,3]
输出:3

示例 2:

输入:[2,2,1,1,1,2,2]
输出:2

基本思路

我们首先可以想到,利用一个 Map 数据结构来计数,那么只需要遍历一遍找到count 最大的就行了,时间复杂度 O(n),但由于有了 Map 结构 空间仍然是 O(n) 的复杂度。

还有另一种方式,可以先排序,把相同元素给排到一起,然后用指针来操作计数,当发现某个数的 count > n / 2,返回即可,时间复杂度由于有排序,平均下来是 O(nlogn),但空间只有 O(1)

有没有其他的做法?

想法是这样的,你知道其中必然有一个数的数量超过总数一半的。

  • 那我们先想象成 2个人竞选,(假设总票数是单数,不会发生同票情况),那么总有一个人票数是超过半数胜出的。

  • 我们除了老实记录票数比较方式,是不是可以用抵消的方式来判断,我们假设投票是这样的 [A, A, B, A, B],A 就是表示投给 A的,我们开始遍历

  • 第 1 张 A, 放在一个列表list中,list = [A],第 2 张还是 A,继续放 list = [A,A],第三个是 B了,那么我们抵消一个 A,列表中剩一个 list = [A],接下来再来一个 A,list = [A, A],来 B,抵消一个 A, list = [A],结束,list 剩了一个 A,说明 A 必然比 B多,反之如果只剩下 B,则B获胜。

  • 同理,我们这个问题,除了B还有其他竞争者,但我们知道必然有个老大假设是 A,肯定超过半数,另外的给 B, C, D, E, ... 分,那么相互抵消强者就更能留在 list中, 所以我们也可以用不同抵消相同相加的方式来做这个问题

  • 时间 O(n) 空间 O(1)

写法实现

  1. 排序指针计数法
var majorityElement = function(nums) {
  // 首先排序, 让相同元素相邻
  nums = nums.sort((a, b) => a - b)
  // count 就是当前 i 的计数
  let [res, count, max] = [nums[0], 1, 1]
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] === nums[i - 1]) {
      count++
    } else {
      count = 1
    }
    // 算出本轮数字的 count 和 max 比较
    if (max < count) {
      res = nums[i]
      max = count
    }
  }
   return res
}

console.log(majorityElement([3,2,3])) // 3
  1. 抵消法
var majorityElement = function(nums) {
    let [majority, listLength] = [null, 0]
    for(item of nums) {
        // list 空了,说明全被抵消
        if (listLength === 0) {
            majority = item;
        }
        // 跟老大相同就加入,不同就抵消
        if (item === majority) {
            listLength++;
        } else {
            listLength--;
        }
    }
    return majority;
};

console.log(majorityElement([6,5,5]))

另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考

  • 10x 程序员工作法