这是我参与更文挑战的第 7 天,活动详情查看 更文挑战
这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。
112. Pow(x, n) (powx-n)
标签
- 分治
- 中等
题目
这里不贴题了,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打开就行,题目大意:
给定一个大小为 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)
写法实现
- 排序指针计数法
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
- 抵消法
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 程序员工作法