面试题16. 数值的整数次方

这篇博客详细介绍了如何实现快速幂算法,从暴力求解到快速幂的常规解法和位运算优化,针对不同情况如负数指数和大数问题进行了解析,并给出了相应的代码示例。
摘要由CSDN通过智能技术生成

题目: 实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
示例:
示例 1:

输入: 2.00000, 10
输出: 1024.00000

示例 2:

输入: 2.10000, 3
输出: 9.26100

示例 3:

输入: 2.00000, -2
输出: 0.25000
解释: 2^-2 = 1/2^2 = 1/4 = 0.25

说明:

一、暴力法,时间超过限制

这个是我们最容易想到的办法,一次for循环就可以解决,但是时间复杂度O(N)超出限制,不能通过题解,代码如下:

double myPow1(double x, int n) 
{
	if(x==1 || n==0)
		return 1;
	double sum=1;
	int flag=0;
	if(n<0)
	{
		flag=1;
		n*=-1;
	}
	for(int i=0;i<n;i++)
	{
		sum*=x;
	}
	if(flag)
	{
		return 1/sum;
	}
	return sum;
}

0(N)时间复杂度超出限制,那么正确题解的时间复杂度一定是O(logN),我们想办法往这个时间复杂度上靠近,很容易想到二分,分为2部分进行相乘,那样可以将乘的次数减少一半,这就是基于二分的快速幂。

二、快速幂

假如我们需要求解36,那么可以分解为:(32)3→(93)→(92)*9=81*9=729。每次底数以x2进行递增,指数以num/2变化,分为奇数和偶数情况。那么我们可以总结出一个规律:
在这里插入图片描述
因为奇数情况,用除法和偶数的结果一样,如3/2=1,2/2=1,所以不用n-1。我们可以看到这是一种二分的形式,即将n进行二分,求x4=x2*x2,而不是x4=x*x*x*x。如果是奇数的话就会多出来一项,所以我们用res保存结果时,当n为奇数,就将x乘入res,再进行x2,最后n==1时,x已经不断x2累乘计算完毕,此时n为奇数,将x乘入res,得出结果,n/2=0循环结束。
我们可以根据上述的思路进行一个示例演示,假如现在计算55,我们进行过程演示:

n幂值(初始值5)奇偶(奇数)res(1)x(5)
n=5奇数1*5=55^5=25
n=5/2=2偶数525*25=625
n=2/2=1奇数5*625=3125625*625=390625
n=1/2=0退出循环,返回res=3125

计算机验证结果正确,那么我们可以总结思路,需要考虑到负数等情况:

  • 如果x=1,或者n=0,那么直接返回1即可。
  • 如果n为负数,需要求x的倒数平方,那么x=1/x;n=-n;如果n用int保存,int有符号整形变量的范围是[-2147483648 2147483647];当-2147483648取绝对值时大于2147483647就会报错出现溢出,所以先对负数进行强制类型转换,用long存储即可。
  • 进行while循环,直到n==0时退出。
  • 循环体内,进行奇数偶数判断,奇数:res*=x;
  • 进行x*=x累加,n/=2不断二分缩小。
  • 循环结束返回res即可

(一)常规解法

我们可以根据思路写出代码:

//2.快速幂运算,不引入位运算,3^3=3^2*3^1,所以我们可以不断计算x的平方,分为奇数,偶数两种情况
double myPow2(double x, int n) 
{
	if(x==1 || n==0)
		return 1;
	long num=n;//int会溢出,所以用long存储因为有符号整形变量的范围是-2147483648    2147483647;当-2147483648取绝对值时大于2147483647就会报错,所以先对负数进行强制类型转换。
	double res=1;
	if(n<0)
	{
		num=-num;
		x=1/x;
	}
	while(num!=0)
	{
		if(num%2==1)//奇数
		{
			res*=x;
		}
		x*=x;
		num/=2;
	}
	return res;
}

(二)借助位运算

我们可以借助位运算来优化代码,降低时间复杂度,我们用到了

num%2==1;//判断奇数偶数
num/=2;//二分

可以用位运算来表示这两句,所有奇数的二进制,0号位置一定是1,所以可以

(num & 1)==1;//加括号表明执行优先级

来判断奇数偶数,如果0号位置为1,返回真,进行res*=x;否则不进入if判断体中。
左移1位表示增大一倍数值,右移1位表示缩小一倍数值,所以,我们可以用>>表示除2运算符:

num>>=1;

那么优化后的代码为:

double myPow(double x, int n) 
{
	if(x==1 || n==0)
		return 1;
	long num=n;
	double res=1;
	if(n<0)
	{
		num=-num;
		x=1/x;
	}
	while(num!=0)
	{
		if((num&1)==1)//奇数,与运算可以判断奇数,奇数它一定在0位上有1
		{
			res*=x;
		}
		x*=x;
		num=num>>1;//右移就是/2的含义
	}
	return res;
}

加油哦!🎉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值