• leetcode常规算法题复盘(第三期)——Pow(x,n)(快速幂)


    题目原文

    50. Pow(x, n)

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

    示例 1:

    输入: 2.00000, 10
    输出: 1024.00000
    

    示例 2:

    输入: 2.10000, 3
    输出: 9.26100
    

    示例 3:

    输入: 2.00000, -2
    输出: 0.25000
    解释: 2的-2次方即1/2的平方

    说明:

    • -100.0 < x < 100.0
    • n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。

    尝试解答

    估计读者朋友们看到这个题目都想骂人:“就这题目你还觉得难???就这?就这???”看位看官先别着急(就不能让我水一期吗?),这是字节跳动某次面试题目,要考察的地方不在于能否实现功能,而在于代码性能(运行速度、迭代次数等),先上我的代码:

    class Solution:
        def myPow(self, x: float, n: int) -> float:
            return pow(x,n)

     好的本次复盘结束,谢谢大家[x]

    开个玩笑,下面是我的代码:

     1 from functools import lru_cache
     2 @lru_cache(maxsize=32)
     3 class Solution:
     4     def myPow(self, x: float, n: int) -> float:
     5         if n==0:
     6             return 1
     7         if n<0:
     8             return self.myPow(1/x,-n)
     9         if n>0:
    10             if n==1:
    11                 return x
    12             elif n%2==1:
    13                 return x*self.myPow(x,(n-1)/2)*self.myPow(x,(n-1)/2)
    14             elif n%2==0:
    15                 return self.myPow(x,n/2)*self.myPow(x,n/2)

     

    因技术力不足,它超时了。。。但是思路跟大佬们的一样,使用的是二分加递归,具体思路如下图所示:

     

    因为递归乘递归会消耗双倍的运算时间,之后运行时间会呈现级数增长趋势,因此稍微改了一下,下面是改良后的代码,运行通过:

     1 from functools import lru_cache
     2 @lru_cache(maxsize=32)
     3 class Solution:
     4     def myPow(self, x: float, n: int) -> float:
     5         if n==0:
     6             return 1
     7         if n<0:
     8             return self.myPow(1/x,-n)
     9         if n>0:
    10             if n==1:
    11                 return x
    12             elif n%2==1:
    13                 a = self.myPow(x,(n-1)/2)
    14                 return x*a*a
    15             elif n%2==0:
    16                 a = self.myPow(x,n/2)
    17                 return a*a

     

     

    标准题解

    思路一: 

    观赏一下大佬们的高技术力代码:

     1 class Solution:
     2     def myPow(self, x: float, n: int) -> float:
     3         def quickMul(N):
     4             ans = 1.0
     5             # 贡献的初始值为 x
     6             x_contribute = x
     7             # 在对 N 进行二进制拆分的同时计算答案
     8             while N > 0:
     9                 if N % 2 == 1:
    10                     # 如果 N 二进制表示的最低位为 1,那么需要计入贡献
    11                     ans *= x_contribute
    12                 # 将贡献不断地平方
    13                 x_contribute *= x_contribute
    14                 # 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可
    15                 N //= 2
    16             return ans
    17         
    18         return quickMul(n) if n >= 0 else 1.0 / quickMul(-n)
    19 
    20 #作者:LeetCode-Solution
    21 #链接:https://leetcode-cn.com/problems/powx-n/solution/powx-n-by-leetcode-solution/
    22 #来源:力扣(LeetCode)
    23 #著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

     

     

     

     

     思路二:

     

     

     

     1 class Solution:
     2     def myPow(self, x: float, n: int) -> float:
     3         if x == 0.0: return 0.0
     4         res = 1
     5         if n < 0: x, n = 1 / x, -n
     6         while n:
     7             if n & 1: res *= x
     8             x *= x
     9             n >>= 1
    10         return res
    11 
    12 #作者:jyd
    13 #链接:https://leetcode-cn.com/problems/powx-n/solution/50-powx-n-kuai-su-mi-qing-xi-tu-jie-by-jyd/
    14 #来源:力扣(LeetCode)
    15 #著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    思路差距

    一、我一开始的思路还是最笨的,x一个一个得乘下去,得知最大递归深度远远超出限制后我改了思路,跟大佬的分治法的思路基本一致,但有一个地方需要注意:

     

    这是比较细节的东西,积累下来积累下来~,还有就是不一定用到递归,递归确实会占用不少资源,但代码简洁性也是显而易见的,关于递归利弊的分析详见知乎https://www.zhihu.com/question/35255112,像二叉树这种的数据结构相关问题基本都是在用递归算法,当然也可以借助栈的辅助进行访问(这个我是完全不会。。大概以后会遇到吧)

    二、从二进制角度分析的思路实在是高!这个没得说先记下来吧,估计后面的问题还会用到。虽然两种思路没那么相似,但细看两种思路的代码会发现对数据的处理过程几乎是一致的,那这两种思路是否可以看作本质上的同一种?幂操作跟二进制之间的数学关系还有待思考。

    三、讲道理力扣中等难度的题目果然轻松很多,无论是思路复杂程度还是细节控制方面,思维量比起困难题都大幅下降,但里面知识也是不少的,还是应该注重基础多多积累。

    技术差距

    一、代码的熟练度和对递归的运用比起第二期更熟练了一些,希望后面能提高更多吧,但或许有时候可以多试试不同的方法解决同一问题?

    二、分治思想是很早之前积累的成果,所以这次比较轻松地就用出来了,很明显这点积累还远远不够用,有种三板斧抡完就没招的意思,所以还是要积累更多的技巧,毕竟这些技巧都是被整个代码编写者人群反复优化反复使用的,必然比一个人在短时间内想出的点子成熟的多。

    三、代码写得过于啰嗦冗长,这给人一种外行的感觉并且会降低代码的性能,应该学会在细节点跟代码简洁上找到平衡点,或者说简洁的代码也不一定不钻细节(还是我太菜),代码还是应该多写,写完自己精简一下。

  • 相关阅读:
    C# 利用DataTable批处理数据导入数据库
    人员基础信息一体化采集系统建设方案
    定时调用WebService方法同步数据
    进程间通信
    信号
    Linux进程基础
    来自硬件男的《信号与系统》整理笔记
    shell脚本编程(ubantu)
    Linux系统c语言开发环境
    Linux系统用户管理及VIM配置
  • 原文地址:https://www.cnblogs.com/monkiki/p/13881746.html
Copyright © 2020-2023  润新知