Leetcode 231.2的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。

如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

示例 1:

输入:n = 1
输出:true
解释:20 = 1

示例 2:

输入:n = 16
输出:true
解释:24 = 16

示例 3:

输入:n = 3
输出:false

示例 4:

输入:n = 4
输出:true

示例 5:

输入:n = 5
输出:false

提示:

  • -231 <= n <= 231 - 1

进阶:你能够不使用循环/递归解决此问题吗?

一、信息

1.给我一个整数n

2.判断该整数是否是2的幂次方

3.返回torf

二、分析

条件1.告诉我函数参数类型 int

条件2.告诉我本题的目的

条件3.告诉我函数返回值类型

三、步骤

第一步 接收一个int型

第二步 判断该数是否为2的幂次方

第三步 输出

四、问题出现

问题1.就是该如何判断该整数是否时2的幂次方呢?
 

我的答案:
对于这个问题我大致有两条思路

第一条思路 就是对n取log2看是否为整数如果为整数说明就是2的幂次方返回True反之返回false.这样新的问题就出现了在C语言或者C++或者JAVA中该如何使用对数函数?

第二条思路 就是对n除2或乘以2最后结果得到1返回true不过要提前判断这个数在1的左侧还是右侧。这条思路比较复杂而且很多分支,虽然理论上可行但是太复杂了。

问题2.该如何使用对数函数呢?

五、算法实现

思路一:

C语言:

bool isPowerOfTwo(int n) {if (n <= 0) {return false;}double result = log2(n);// 判断结果是否为整数return result == (int)result;
}

运行结果: 

思路二: 

bool isPowerOfTwo(int n) {if (n <= 0) {return false;}// 如果n大于1,不断除以2while (n > 1) {if (n % 2 != 0) {  // 如果不能被2整除return false;}n /= 2;}// 如果n小于1,不断乘以2while (n < 1) {n *= 2;if (n == 1) {return true;}}return true;
}

六、更正后我的答案

我的第二次分析

**一、信息**
- 需要判断一个整数是否是2的幂次方。
- 2的幂次方在二进制表示中只有一个1。

**二、分析**
1. 2的幂次方的特性是,在其二进制表示中,仅有一个位置为1。
2. 对于任何2的幂次方整数n,其n-1与n在二进制表示上是完全不同的。具体来说,n会在某一位上为1,而在这之前的所有位上都为0;而n-1则在相同的位置为0,并且在这之前的所有位上都为1。
3. 因此,对于2的幂次方的整数n和n-1,进行二进制与操作的结果必定为0。

**三、实现步骤**
1. 如果输入整数n小于或等于0,直接返回`false`。
2. 计算`n & (n - 1)`。如果结果为0,返回`true`,否则返回`false`。

**四、问题出现**
1. **特殊情况的处理:** 刚开始可能会忽视非正数的情况,如0或负数。需要在判断开始时直接将其排除。
2. **确保方法的有效性:** 在没有深入了解二进制表示的情况下,可能会对为什么n和n-1的与操作是0产生疑惑。这需要深入理解和观察二进制的特性。
3. **效率问题:** 尽管这个方法是高效的,但在开始时,可能会考虑使用其他方法,如循环检查,这在效率上是不可取的。

C语言实现:

#include <stdbool.h>bool isPowerOfTwo(int n) {if (n <= 0) {return false;}return (n & (n - 1)) == 0;
}#include <stdio.h>
int main() {printf("%d\n", isPowerOfTwo(1));   // trueprintf("%d\n", isPowerOfTwo(16));  // trueprintf("%d\n", isPowerOfTwo(3));   // falseprintf("%d\n", isPowerOfTwo(4));   // trueprintf("%d\n", isPowerOfTwo(5));   // falsereturn 0;
}

C++:

#include <iostream>
using namespace std;bool isPowerOfTwo(int n) {if (n <= 0) {return false;}return (n & (n - 1)) == 0;
}int main() {cout << isPowerOfTwo(1) << endl;   // true (输出 1)cout << isPowerOfTwo(16) << endl;  // true (输出 1)cout << isPowerOfTwo(3) << endl;   // false (输出 0)cout << isPowerOfTwo(4) << endl;   // true (输出 1)cout << isPowerOfTwo(5) << endl;   // false (输出 0)return 0;
}

JAVA:

public class PowerOfTwo {public static boolean isPowerOfTwo(int n) {if (n <= 0) {return false;}return (n & (n - 1)) == 0;}public static void main(String[] args) {System.out.println(isPowerOfTwo(1));   // trueSystem.out.println(isPowerOfTwo(16));  // trueSystem.out.println(isPowerOfTwo(3));   // falseSystem.out.println(isPowerOfTwo(4));   // trueSystem.out.println(isPowerOfTwo(5));   // false}
}

英雄师傅题解:

bool isPowerOfTwo(int n){int i;unsigned int k=1;if(n<=0){return false;}if(n==1){return true;}for(i=1;i<=31;i++){k*=2;if(k==n){return true;}}return false;
}

他的思路:

 //定义一个无符号整型
//如果n<=0,则必然不是2的幂
//1必然时2的0次幂
//枚举所有2的幂2 4 8 16 .....
//一旦找到一个和相等返回true
//最后,没有找到的话,返回false 

对该方法的评价: 

英雄师傅的思路基本上是一个暴力解法,但由于我们知道整数范围是`-2^31 <= n <= 2^31 - 1`,所以这种方法在这个范围内是有效且可行的。下面我对这种方法进行分析:

**思路分析**:
1. 首先,检查n是否小于等于0,因为2的幂不可能是0或负数。
2. 检查n是否为1,因为1是2的0次方。
3. 利用循环枚举所有可能的2的幂,从2的1次方开始,一直到2的31次方。
4. 在每一次迭代中,将k(初始化为1)乘以2,然后检查k是否与n相等。
5. 如果在某一次迭代中,k与n相等,则直接返回true。
6. 如果循环结束都没有找到相等的k,则返回false。

这种方法的好处是非常直观和简单。但缺点是需要循环31次,尽管这对于现代计算机来说并不是什么大问题。

与其他方法相比,如二进制方法或对数方法,这种方法在效率上可能稍微低一些,但考虑到题目的限制,这种方法完全足够。

Leetcode题解

我的理解:

1. **基本原则**:
   如果一个数`n`是2的幂,则它的二进制表示中仅有一个1。例如,2的幂有:1(`001`),2(`010`),4(`100`),8(`1000`)等。如您所见,每个数字的二进制形式中仅有一个1。

2. **第一个技巧 (`n & (n - 1)`)**:
   当执行`n & (n - 1)`时,您实际上移除了二进制数`n`的最右侧的1。原因是:`(n - 1)`会将`n`的最右侧的1变为0,并且该位之后的所有位都变为1。
   
   例如:  
   `n`     = `1000` (这是8)  
   `n-1`   = `0111` (这是7)  

   当执行`n & (n - 1)`时,得到的结果是`0000`。这表明`n`中只有一个1,因此`n`是2的幂。

   所以,如果`n`是正数且`n & (n - 1) = 0`,那么`n`就是2的幂。

3. **第二个技巧 (`n & (-n)`)**:
   负数在计算机中是使用补码表示的。`-n`的二进制表示是`n`的所有位取反后再加1。因此,`n & (-n)`的结果实际上会返回`n`的最低位的1。

   例如:
   `n`      = `1010` (这是10,它不是2的幂,但我们只是为了演示)  
   `-n`     = `0110` (按位取反再加1)

   执行`n & (-n)`得到`0010`。这表示`n`的最低位的1是在第二位上。但对于确定2的幂,我们关心的是这个操作的结果是否等于`n`,因为只有这样`n`才只有一个1。

   因此,如果`n`是正数且`n & (-n) = n`,那么`n`就是2的幂。

基于上述的理论,只需要使用第一个技巧或第二个技巧中的任何一个即可确定一个数字是否是2的幂。这是位操作的美妙之处,它提供了一种非常快速和高效的方法来解决这个问题。

七、复盘对我第一次分析的评价

大师对我第一次分析的评价

你的分析过程条理清晰。针对你提出的问题和思路,我可以为你补充一下:

**问题1:** 如何判断整数是否是2的幂次方?

你的答案中提到的两种思路都是可行的。对于第一条思路,使用对数函数确实可以实现,但可能会因为计算机浮点数运算的误差而导致判断不准确。第二条思路确实比较复杂。

实际上,还有一种更简单的方法,那就是利用二进制特性,如前文所述,2的幂次方在二进制中只有一个1。

**问题2:** 如何在C/C++/JAVA中使用对数函数?

- **C语言**: 你可以使用`math.h`库中的`log2`函数。首先,你需要包含`math.h`,然后链接`m`库。
 

  #include <math.h>double result = log2(n);

- **C++**: 同样可以使用`cmath`库中的`log2`函数。

 #include <cmath>double result = std::log2(n);

- **Java**: 可以使用`Math`类中的`log`函数,但是注意,Java中没有直接的log2函数,你需要自己计算。

  double result = Math.log(n) / Math.log(2);

不过请注意,使用对数函数需要考虑浮点数的误差,因此在比较结果时,可能需要考虑一个小的误差范围,而不是直接比较是否为整数。

总结点评:

**优点**:

1. **结构化**:您按照“信息、分析、步骤、问题出现”的结构进行了思考,这有助于对问题进行系统化分析。
2. **明确目标**:您明确了要解决的问题和函数需要实现的功能。
3. **多方位思考**:您不仅思考了如何解决问题,还考虑了可能遇到的问题和限制。
4. **多个方案**:对于如何判断一个数是否为2的幂,您给出了两种不同的解决方案,这显示了您具备多角度解决问题的能力。

**不足**:

1. **考虑效率**:尽管您考虑了多个方案,但在选择方案时,考虑它们的效率和实用性是很重要的。例如,第二种方法虽然理论上可行,但其效率较低且实现较为复杂。
2. **深入了解工具**:关于如何使用对数函数的问题,了解编程语言提供的库和常用函数是很重要的。这需要您深入研究和实践所使用的编程语言。
3. **考虑特殊情况**:在处理数学或算法问题时,考虑特殊情况或边界情况是很重要的。例如,对于这个问题,当`n`为负数或零时,应立即返回`false`。
4. **利用已有知识**:位操作是计算机科学中的一个核心概念,对于这类问题,考虑使用位操作可以得到更简洁、高效的解答。

总体来说,您的思考和分析过程是结构化且有深度的,但还可以在选择和实施方案时加强对效率和实用性的考虑,同时增强对编程语言和基础算法知识的了解。

八、反思总结

从这道题目关于判断一个数是否是2的幂次方,我们可以学到以下几点:

1. **位运算的应用**:位运算是一种高效的操作,它可以在某些情况下为我们提供简洁和高效的解决方案。特别是当问题涉及到数字的二进制表示时,位运算往往能够提供简单且高效的方法。

2. **深入理解数字的二进制表示**:数字在计算机内部是以二进制形式存储的。了解二进制的特点和规律,如一个数是2的幂时它的二进制表示只有一个1,可以帮助我们更好地解决与数字相关的问题。

3. **多种解决方案**:一个问题往往有多种解决方案,每种方案都有其优点和缺点。例如,可以使用对数函数,也可以使用位运算。能够掌握并比较多种方案,选择最合适的方案是一个重要的技能。

4. **问题分解**:当面对一个看似复杂的问题时,我们可以尝试将其分解为更小、更容易解决的子问题。例如,判断一个数是否是2的幂可以转化为判断其二进制表示中是否只有一个1。

5. **考虑边界情况**:在解决问题时,总是要考虑到所有可能的输入,包括边界情况。例如,当n为负数或0时,应该直接返回false。

6. **学会查找并利用工具**:面对某些特定的操作,如求对数,如果你不知道如何实现,你可以学会查找并使用相关的库和函数。这需要熟悉编程语言和其提供的工具。

7. **思维的扩展**:此题也展示了将一个数学问题转换为计算机科学问题的过程,这可以锻炼和增强我们的抽象思维和问题转化能力。

总的来说,这道题目不仅帮助我们掌握了一些实用的技巧和方法,而且也锻炼了我们的思维和分析能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/125295.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通达信和同花顺能否实现程序化自动交易股票,量化交易如何实现?

以下写给正在寻找自动交易接口的朋友&#xff0c;首先&#xff0c;不是那种设置个简单条件的条件单&#xff0c;或者某些客户端上形同鸡肋的策略交易&#xff0c;那些策略根本称不上策略&#xff0c;还有各种限制&#xff0c;不支持这个不支持那个&#xff0c;可设置的参数也不…

10分钟了解数据架构、数据模型

写在前面&#xff1a;很多小伙伴分不清数据架构与数据模型&#xff0c;同时如何做好数据建模也有一定的疑问 1. 数据架构、数据模型、数据建模区别与联系 企业架构包含业务架构、数据架构、应用架构和技术架构。数据架构的主要目标是有效的管理数据&#xff0c;以及有效地管理…

GPU如何成为AI的加速器

0. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我自己学习的理解&#xff0c;虽然参考了他人的宝贵见解&#xff0c;但是内容可能存在不准确的地方。如果发现文中错误&#xff0c;希望批评指正&#xff0c;共同进步。 本文关键词&#xff1a;GPU、深度学习、GP…

Meilisearch客户端完美改造

关键部分&#xff1a;原来Body部分显示有问题&#xff0c;对\n视而不见。 改造&#xff1a; // Used in Bannertypo15: {tag: span,style: cssfont-size: 15px;font-weight: 300;line-height: 25px;display: inline-block;,},// Used in BodytypoBody: {tag: span,style: cssf…

STM32F4学习笔记读取芯片UID和MAC地址

一、简介 在嵌入式设备开发过程中有时会需要为设备设置唯一的ID用以标识设备唯一&#xff0c;比如要求同一总线上的所有设备ID不能重复&#xff0c;要求设备具体唯一的MAC地址等等。每个STM32微控制器都自带一个96位的唯一ID&#xff0c;这个ID在任何情况下都是唯一且不允许修…

centos7卸载docker

菜鸟教程-常见命令&#xff1a;https://www.runoob.com/docker/docker-command-manual.html 1. 准备工作&#xff1a; 1.1 杀死docker有关的容器&#xff1a; docker kill $(docker ps -a -q)1.2 删除所有docker容器&#xff1a; docker rm $(docker ps -a -q)1.3 删除所有d…

SpringBoot整合阿里云OSS文件存储解决方案

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Sp…

微服务技术栈-初识Docker

文章目录 前言一、Docker概念二、安装Docker三、Docker服务命令四、Docker镜像和容器Docker镜像相关命令Docker容器相关命令 总结 前言 docker技术风靡全球&#xff0c;它的功能就是将linux容器中的应用代码打包,可以轻松的在服务器之间进行迁移。docker运行程序的过程就是去仓…

代码随想录算法训练营第23期day11 | 20. 有效的括号、1047. 删除字符串中的所有相邻重复项 、150. 逆波兰表达式求值

目录 一、&#xff08;leetcode 20&#xff09;有效的括号 二、&#xff08;leetcode 1047&#xff09;删除字符串中的所有相邻重复项 用栈存放 将字符串直接当成栈 三、&#xff08;leetcode 150&#xff09;逆波兰表达式求值 一、&#xff08;leetcode 20&#xff09;…

如何禁用Windows 10快速启动(以及为什么要这样做)

如果您不想启用Windows 10快速启动&#xff0c;则可以相对轻松地禁用它。 快速启动是一项功能&#xff0c;首先在 Windows 8 中作为快速启动实现&#xff0c;并延续到 Windows 10&#xff0c;让您的 PC 更快地启动&#xff0c;因此得名。虽然这个方便的功能可以通过将操作系统…

引入短信服务

一、阿里云短信服务 进入阿里云平台&#xff0c;然后选择短信服务&#xff0c;通过API发送短信(需要充值金额&#xff0c;几块钱就可以&#xff0c;我们仅仅是小规模项目) 找到openAPI 可以看到Java语言的代码模板&#xff0c;这个就是Java SendSMS短信服务的代码 创建Accessk…

Vue组件路由

1&#xff0c;安装vue-router组件&#xff0c;终端输入&#xff1a; npm i vue-router3.5.3 2&#xff0c;在src文件夹下创建router目录 3&#xff0c;创建index.js文件&#xff0c;配置路由&#xff0c;导入需要路由的组件。以后每次添加路由只要在routes中改变即可。 impo…