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.mzph.cn/news/95224.shtml

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

相关文章

文件操作 和 IO - 详解

一&#xff0c;认识文件 1.1 树形结构组织和目录 文件是对于"硬盘"数据的一种抽象&#xff0c;在一台计算机上&#xff0c;有非常多的文件&#xff0c;这些文件是通过 "文件系统" 来进行组织的&#xff0c;本质上就是通过 "目录"(文件夹) 这样…

Maven(项目构建管理工具)

为什么要使用Maven&#xff1f; 传统项目管理状态分析&#xff1a; ①jar包不统一&#xff0c;jar包不兼容&#xff1b; ②工程升级维护过程操作繁琐&#xff1b; ........... Maven(JAVA书写)&#xff1a;管理jar包以及jar之间的依赖关系&#xff0c;完成项目编译&#xff0c;…

【Java 进阶篇】JDBC数据库连接池Druid详解

在Java应用程序中&#xff0c;与数据库进行交互是一个常见的任务。为了更有效地管理数据库连接并提高性能&#xff0c;数据库连接池是一种常见的解决方案。Druid是一个流行的JDBC数据库连接池&#xff0c;它具有丰富的功能和高性能。本博客将详细介绍Druid连接池&#xff0c;包…

6.Tensors For Beginners-What are Convector

Covectors &#xff08;协向量&#xff09; What‘s a covector Covectors are “basically” Row Vectors 在一定程度上&#xff0c;可认为 协向量 基本上就像 行向量。 但不能简单地认为 这就是列向量进行转置&#xff01; 行向量 和 列向量 是根本不同类型的对象。 …

【iptables 实战】9 docker网络原理分析

在开始本章阅读之前&#xff0c;需要提前了解以下的知识 阅读本节需要一些docker的基础知识&#xff0c;最好是在linux上安装好docker环境。提前掌握iptables的基础知识&#xff0c;前文参考【iptables 实战】 一、docker网络模型 docker网络模型如下图所示 说明&#xff1…

僵尸进程的产生与处理

僵尸进程是指在进程结束后&#xff0c;其父进程没有及时处理该进程的终止状态信息&#xff0c;导致该进程的进程描述符仍然存在于系统进程表中&#xff0c;但是已经没有实际的执行代码。这样的进程被称为僵尸进程。 僵尸进程的产生是由于父进程没有及时调用wait()或waitpid()等…

RabbitMQ-死信队列

接上文 RabbitMQ-java使用消息队列 1 死信队列简介 死信队列模式实际上本质是一个死信交换机绑定的死信队列&#xff0c;当正常队列的消息被判定为死信时&#xff0c;会被发送到对应的死信交换机&#xff0c;然后再通过交换机发送到死信队列中&#xff0c;死信队列也有对应的消…

基于Matlab求解高教社杯数学建模竞赛(cumcm2010A题)-储油罐的变位识别与罐容表标定(附上源码+数据+题目)

文章目录 题目解题源码数据下载 题目 通常加油站都有若干个储存燃油的地下储油罐&#xff0c;并且一般都有与之配套的“油位计量管理系统”&#xff0c;采用流量计和油位计来测量进/出油量与罐内油位高度等数据&#xff0c;通过预先标定的罐容表&#xff08;即罐内油位高度与储…

【Ubuntu】基于C++实现人脸识别

人脸识别考勤机 文章目录 人脸识别考勤机概述第一章 搭建Ubuntu环境1.1 什么是物联网1.2 物联网应该怎么学1.3 Linux开发环境搭建1.4 Linux基本使用1.5 Ubuntu网络配置 第二章 “hello,world!”程序2.1 什么是程序2.2 “hello,world!”程序2.3 C语法扩展2.4 常见错误调试 第三章…

ELK 处理 Spring Boot 日志

ELK 处理 Spring Boot 日志&#xff0c;妙啊&#xff01; 来源&#xff1a;ibm.com/developerworks/cn/java /build-elk-and-use-it-for-springboot -and-nginx/index.html ELK 简介 Logstash Elasticsearch Kibana ELK 实现方案 ELK 平台搭建 安装 Logstash 安装 Elas…

国庆day5

客户端 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);socket new QTcpSocket(this);//此时&#xff0c;已经向服务器发送连接请求了&#xff0c;如果成功连…

图的深度遍历-邻接矩阵实现

description 本题要求实现邻接矩阵存储图的深度优先遍历。 函数接口定义&#xff1a; void DFS(MGraph G,Vertex v); 其中MGraph是邻接矩阵存储的图&#xff0c;定义如下&#xff1a; #define MaxVertexNum 10 /定义最大顶点数/ typedef int Vertex;/* 用顶点下标表示顶点,…

【重拾C语言】五、模块化程序设计——函数(定义、调用、参数传递、结果返回、函数原型;典例:打印字符图形、验证哥德巴赫猜想)

目录 前言 五、模块化程序设计——函数 5.1 计算三角形的重心 5.2 函数 5.2.1 函数定义 5.2.2 函数调用 a. 函数调用的形式和过程 b. 参数传递 值传递 指针传递 c. 函数结果返回 5.2.3 函数原型&#xff08;先调用后定义&#xff09; 5.3 程序设计实例 5.3.1 打印…

C/S架构学习之TCP的三次握手和四次挥手

TCP的三次握手&#xff1a;一定由客户端主动发起的&#xff0c;发生在建立连接的过程中。此过程发生在客户端的connect()函数和服务器的accept()函数之间。第一次握手&#xff1a;客户端向服务器发送一个带有SYN标志的数据包&#xff0c;表示客户端请求建立连接。并且客户端会选…

3D孪生场景搭建:模型区域摆放

前面介绍完了NSDT场景编辑器的线性绘制和阵列绘制&#xff0c;本章将讲述下编辑器的另一种绘制方式&#xff1a;区域绘制。 1、区域绘制功能简介 在场景中绘制资产时&#xff0c;除使用上述两个的方式外&#xff0c;NSDT 编辑器还支持使用区域绘制的方式进行绘制。先选取需要…

python修改unittestreport中的用例条数

背景: 自动化框架中使用yaml文件作为数据配置&#xff0c;使用ddt作为数据驱动来运行测试用例&#xff0c;由于测试用例都是基于场景去编写&#xff0c;目前都是一个测试类算是一条测试用例&#xff0c;但基于测试报告里面一个类运行的测试方法有多个&#xff0c;因此统计的测试…

计算机毕业设计 基于SpringBoot的图书馆管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

香蕉叶病害数据集

1.数据集 第一个文件夹为数据增强&#xff08;旋转平移裁剪等操作&#xff09;后的数据集 第二个文件夹为原始数据集 2.原始数据集 Cordana文件夹&#xff08;162张照片&#xff09; healthy文件夹&#xff08;129张&#xff09; Pestalotiopsis文件夹&#xff08;173张照片&…

【Java 进阶篇】JDBC 数据库连接池 C3P0 详解

数据库连接池是数据库编程中常用的一种技术&#xff0c;它可以有效地管理数据库连接&#xff0c;提高数据库访问的性能和效率。在 Java 编程中&#xff0c;有多种数据库连接池可供选择&#xff0c;其中之一就是 C3P0。本文将详细介绍 C3P0 数据库连接池的使用&#xff0c;包括原…

Linux CentOS7 vim重复行

在用vim编辑处理文件时&#xff0c;会有重复行。有的是情境需要&#xff0c;有的可能是误操作而形成。对于正常形成的重复行&#xff0c;我们不作讨论&#xff0c;我们仅讨论什么情况下会出现重复行&#xff0c;如何避免&#xff0c;如何处理。 在文件中的单行或多个连续空白行…