1. 位1的个数

news2025/4/19 10:00:29

文章目录

    • 题目
    • 解法一:右移统计
      • 解题思路
      • 代码实现
      • 复杂度分析
    • 解法二:清除最低位
      • 解题思路
      • 代码实现
      • 复杂度分析
    • 解法三:分治
      • 解题思路
      • 代码实现
      • 复杂度分析
    • 解法四:JDK Integer.bitCount(int i)
      • 代码实现

题目

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。题目来自leetcode。

解法一:右移统计

解题思路

右移统计,判断最后一位是否为 1。

  1. 只要 n != 0,我们就可以知道 n 的二进制表示当中肯定包含有 1,不管它在哪个位置。
  2. 只要 n & 1 = 1,我们就可以知道 n 的最后一位肯定是1。
  3. 那么根据前两个条件,我们可以把 n 不断的向右移 1 位,高位补0, 然后不断累加记录最后一位是 1 的次数,直到移动到 n = 0 结束。

代码实现

public class Solution {
    public int hammingWeight(int n) {
        int cnt = 0;
        while(n != 0){
            if((n & 1) == 1){
                cnt++;
            }
            // 逻辑右移动,高位补0;
            // 这里不能使用算术右移,因为算术右移会在高位补符号位,如果是负数,那么高位会补1,这样会导致while循环一直出不去。
            n = n >>> 1;
        }
        return cnt;
    }
}

复杂度分析

时间复杂度 O ( l o g N ) O(logN) O(logN):时间复杂度为while循环的次数,取决于 n 的二进制表示中最高位的 1 在哪个位置,即以2为底的log(n)向下取整 + 1,最坏的情况下就是32次。

空间复杂度 O ( 1 ) O(1) O(1):只开了一个变量cnt用于存放结果,空间复杂度为1。

解法二:清除最低位

解题思路

使用最低位的思路,n = n & (n - 1) 表示删掉 n 的最低位,参见算法题中常用的位运算。

  1. 只要 n != 0, 表示 n 的二进制表示中包含有 1,计数加一。
  2. 删掉 n 的最低位,判断是否还满足 n != 0,满足则计数加一并继续删掉最低位;不满足则结束。

代码实现

public class Solution {

    public int hammingWeight(int n) {
        int cnt = 0;
        while(n != 0){
            cnt++;
            n = n & (n - 1);
        }
        
        return cnt;
    }
}

复杂度分析

时间复杂度 O ( M ) O(M) O(M):时间复杂度M为二进制表示中 1 的个数,M <= log(n) + 1。当最高位是1,其他位都是0的时候,解法一需要32次,解法二只需要1次。

空间复杂度 O ( 1 ) O(1) O(1):只开了一个变量cnt用于存放结果,空间复杂度为1。

解法三:分治

解题思路

利用分治的思想,将每个位置的1加起来即可,以181=1011 0101为例:

  1. 两两一组相加,将8位二进制会分为4组(如果是32位,那么就会分成16组)。相加后二进制表达为 01_10_01_01,代表的是4组结果:1 + 2 + 1 + 1 = 5。
  2. 将第一步得到的4组结果再进行两两一组相加,结果为0011_0010, 代表 3 + 2 = 5。
  3. 将第二步得到的2组结果合并相加,结果为00000101=5。
    注意:直到最后一步的时候,所有的二进制位都合并为一组的时候,其结果才是一个真正的数值,前面所有步骤中的结果只在分组中是有效数值。32位计数同理。

位1的个数

算法图解:
思路有了,那么接下来分析如何让两两一组相加呢?

  1. 我们可以新开一个数,使其二进制表达为原数值的二进制向后移动一位,首位补0,这样就刚好让分在同一组的两个位置对其。
  2. 对齐后我们需要两个数相加,我们需要将原数值中绿色部分和新数值的蓝色部分清空(重复冗余数据),清空方式为 &01010101, 16进制表达为 &0x55。
  3. 相加结果为01_10_01_01,与上述解题思路得到一致的结果。

位1的个数-分治算法

  1. 将第三步得到的结果再分组相加,逻辑同上。但是这个时候每个分组中有2个位置,所有删除冗余数据的时候需要 &00110011 = &0x33。
    位1的个数-分治算法

  2. 循环上述步骤,直到所有二进制位都被分到同一组为答案。

代码实现

public class Solution {
   public int hammingWeight(int n) {
		// 每组1个位置,右移1位对齐并删除冗余数据(&01010101)。
        n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
        // 每组2个位置,右移2位对齐并删除冗余数据(&00110011)。
        n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
        // 每组4个位置,右移4位对齐并删除冗余数据(&00001111)。
        n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
        // 每组8个位置,右移8位对齐并删除冗余数据(&0000000011111111)。
        n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);
        // 每组16个位置,右移16位对齐并删除冗余数据(&000000000000000001111111111111111)。
        n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);

        // 最后32位二进制全部合并到一组,即位答案。
        return n;
    }
}

复杂度分析

时间复杂度 O ( l o g N ) O(logN) O(logN):N为二进制位数,这里是32。当1的个数比较多的时候,这种方式的时间复杂度要低一些,固定的只需要5步。

空间复杂度 O ( 1 ) O(1) O(1)

解法四:JDK Integer.bitCount(int i)

解法4是解法3的优化。

  1. n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
    ○ 0b11: 0b01 + 0b01 = 0b10 2个
    ○ 0b10: 0b00 + 0b01 = 0b01 1个
    ○ 0b01: 0b01 + 0b00 = 0b01 1个
    ○ 0b00: 0b00 + 0b00 = 0b00 0个
    两两一组相加时有上述4种情况,从中我们可以发现:
    ○ 0b10 = 0b11 - 0b01
    ○ 0b01 = 0b10 - 0b01
    ○ 0b01 = 0b01 - 0b00
    ○ 0b00 = 0b00 - 0b00
    即计算后的结果(n) = 原值(n) - ((n >>> 1) & 0x55555555) = (n & 0x55555555) + ((n >>> 1) & 0x55555555),优化后少了一次&运算。

我们可以验证一下1101(3个1):
优化前:(n & 0x55555555) + ((n >>> 1) & 0x55555555) = 0b01_01 + ((0b01_10) & 0x5) = 0b01_01 + 0b01_00 = 0b10_01 = 2 + 1 = 3。
优化后:n - ((n >>> 1) & 0x5) = 0b11_01 - ((0b01_10) & 0x5) = 0b11_01 - 0b_0100 = 0b10_01 = 2 + 1 = 3。

  1. n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
    无优化

  2. n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
    这个优化方式是这样考虑的,因为这里刚好是8个位置为一组,也就是个字节,做多有8个1,二进制表达最大为0000_1000,有后面4位就已经够用了,所以可以先用(n + (n >>> 4))把后面4位算出来,再&0f0f将前4位清空,优化为:n = (n + (n >>> 4)) & 0x0f0f0f0f,少了一次&运算。

  3. n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);
    同方法3一样,这里是16位为一组,最多16个1,二进制表达最大为0000_0000_0001_0000,前8位全是0,可以暂时忽略,可以先用后8位相加,然后再清空前8位。
    优化为:n = (n + (n >>> 8)) & 0x00ff00ff。

  4. n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);
    同上,优化为:n = (n + (n >>> 16)) & 0x0000ffff。

  5. 进一步观察,会发现,第4步第5步的有效位都在最后的8位上,前面的都是无效位,所以可以将清空操作放到最后,只保留最后8位有效位置,前面的全部清空,即 &0b1111_111111 = &0xff。
    再进一步观察,第4步有效位是最后5位,第5步有效位是最后6位(0b10_0000 = 32),第7位第8位肯定全部为0,所以也可以&0b11_111111 = &0x3f。

代码实现

public class Solution {
   public int hammingWeight(int n) {
		n = n - ((n >>> 1) & 0x55555555);
        n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
        n = (n + (n >>> 4)) & 0x0f0f0f0f;
        n = n + (n >>> 8);
        n = n + (n >>> 16);
        return n & 0x3f;
    }
}

复杂度同解法三。

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

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

相关文章

DPU02:国产USB转UART的桥接控制器兼容替代CP2102

目录DPU02芯片特性USB配置数据和序列号设置DPU02应用领域DPU02是高度集成的USB转UART的桥接控制器芯片&#xff0c;可将RS-232设计更新为USB设计&#xff0c;并简化PCB组件空间。DPU02包括了一个USB 2.0全速功能控制器、USB收发器、振荡器、EEPROM和带有完整调制解调控制信号的…

一文详解深度学习冷板式液冷散热技术规范及要求

深度学习 | 东数西算 | 液冷散热 数据挖掘 | 数据分析 | 高性能计算 随着深度学习、东数西算、医药研发、数据分析、数据挖掘、遥感测绘、高性能计算等技术的快速发展&#xff0c;数据中心的创建与日俱增&#xff0c;传统的风冷散热方式已经不同满足数据中心散热的需求&#x…

Huffman二进制编码以及文本的压缩与解压

目录Huffman树转化成二进制编码文本压缩文本解压Huffman树转化成二进制编码 在上一篇博客的末尾&#xff0c;将Huffman树转化成了01 构成的字符串&#xff0c;显然在实际应用中不是这种操作。我们实际想要的是01构成的一串bits&#xff1b;举个例子&#xff1a;字符"A&quo…

HTTP权威指南------URL与资源

目录 URL标准格式 URL快捷方式 动扩展URL 字符 方案详解 web基础中介绍了URI、URL与URN&#xff1b; URI是一类更通用的资源标识符&#xff0c;URL是它的一个子集&#xff1b; URI是一个通用的概念&#xff0c;它主要由URL与URN组成&#xff1b; URL是通过描述资源的位…

【Kafka】Linux下搭建kafka服务,完整学习案例

【Kafka】Linux下搭建kafka服务&#xff0c;完整学习代码案例&#xff08;一&#xff09;Kafka架构基础【1】图解kafka是什么&#xff1f;&#xff08;1&#xff09;为什么需要消息队列&#xff08;2&#xff09;Topic主题&#xff08;3&#xff09;分区&#xff08;4&#xff…

【UE4 第一人称射击游戏】06-设置动画角色2

步骤&#xff1a; 1.打开“WalkRun_BS”&#xff0c;将最左边中间的点和最右边中间的点的动画改为“walk_backward_inPlace” 2.打开“SWAT_AnimBP”&#xff0c;双击“Walk_Run” 双击“Walk_Run” 将混合空间“WalkRun_BS”拖入 将“Direction”和“Speed”提升为变量&#…

车载以太网 - 初识DoIP - 01

1、DoIP是什么? 从表达形式上 它就是UDS诊断套上车载以太网的马甲,然后实现UDS诊断的所有内容。通过下图DoIP的报文帧格式,从下面往上看,最尾部的数据我们能够很清晰的看到,User Data实际上就是UDS诊断数据,比如:10 03亦或是22 F1 86等信息。然后再往前就是源地址和目标…

Python通过Spleeter实现音唱人声(歌声)伴奏分离

程序示例精选 Python实现音唱人声(歌声)伴奏分离 如需安装运行环境或远程调试&#xff0c;见文章底部微信名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《Python实现音唱人声(歌声)伴奏分离》编写代码&#xff0c;功能包括了人声(歌声)-伴奏分离。运…

Dubbo、Spring Cloud和kubernetes该如何选型?

文章目录背景微服务的公共关注点微服务平台的选型横向比对微服务优劣比对背景 做技术选型。如果缺乏足够的经验&#xff0c;对so a啊&#xff0c;微服架构原理以及整个行业服务化演进的历史缺乏了解。 那么&#xff0c;对这个三个产品该如何选择啊&#xff1f;的确会感到困惑。…

想做副业怎么才能找到适合的项目,六条建议让你找副业不再迷茫

大家好&#xff0c;我是蝶衣王的小编 现在疫情反复&#xff0c;赚钱太难了。许多年轻人想发展副业。而现实情况往往是不知道做什么以及如何做&#xff0c;这是非常令人困惑的。我在这里分享六个要点&#xff0c;让你找到合适的项目。 六个步骤分别是 会观察、观察相关信息和赚…

有趣的网站分享——福音戰士標題生成器

说起文字标题生成器其实也是有一定的历史了。 他们往往源于某种媒介所带来的个性化标题的出现&#xff08;比如动画或电影、广告、产品Logo等&#xff09;。 被互联网群众察觉分享后在模因传播的现象下深入人心。 比如下面这样的&#xff1a; 就出自知名交友网站P…… 额咳。…

快速入门Spring MVC 一篇就够了

前言 我们前面学习了Spring两大核心机制IoC和AOP&#xff0c;接下来我们一起来学习Spring MVC。这篇文章带你快速入门Spring MVC。 Spring MVC概述 Spring MVC是目前主流的实现MVC设计模式的框架&#xff0c;是Spring框架的一个分支产品&#xff0c;以Spring loC容器为基础&…

Fabric.js 铅笔笔刷

本文简介 点赞 关注 收藏 学会了 fabric.js 的铅笔其实是继承基础画笔的一个工具&#xff0c;在基础画笔的基础上多了“拐角平滑度”等配置项。 本文讲解铅笔的基础用法以及常用事件。 常规配置 真实世界的铅笔有不同的型号&#xff0c;颜色的深浅、笔芯的硬度都是不同的…

90后,27岁转行软件测试,从月入3000+到月薪过万,打开了人生新篇章~

承蒙时光不弃&#xff0c;感谢努力的自己。以前总是在某些鸡汤文中看到这句话&#xff0c;当时觉得过于矫情&#xff0c;而如今当我突破重重困难成功转行&#xff0c;收获了更好的人生后&#xff0c;才发自内心的也有了这样的感叹。 几个月的努力和辛劳&#xff0c;一时之间难以…

转互联网好难,如何避免无效转行?

如果你现在是在传统行业工作&#xff0c;想转行互联网&#xff0c;应该怎么做呢&#xff1f; 很多人经常会担心自己的行业、专业、年龄等等会是障碍&#xff0c;或者自己没有经验&#xff0c;去面试的时候公司却都需要相关经验的人&#xff0c;怎么办呢&#xff1f; 这篇文章…

图形驱动软件栈

图形驱动软件栈 HINZER&#xff0c;2022年&#xff0c;我在北京。芯片设计行业&#xff0c;GPU 固件和驱动开发&#xff0c;对嵌入式系统感兴趣。 1 说明背景 1.1 近来想法 做了一段时间的 GPU 固件和驱动开发&#xff0c;加上平时学习的一些零散的知识&#xff0c;最近打算整…

【Web前端HTML5CSS3】06、盒模型

六、盒模型 1、文档流&#xff08;normalflow&#xff09; 网页是一个多层的结构&#xff0c;一层摁着一层 通过 CSS 可以分别为每一层来设置样式&#xff0c;作为用户来讲只能看到最顶上一层 这些层中&#xff0c;最底下的一层称为文档流 文档流是网页的基础我们所创建的元…

牛客java刷题知识点总结(八)

方法调用 类中变量&#xff1a; 除了private权限外&#xff0c;其他权限的变量&#xff08;没有表示默认default&#xff09;&#xff0c;均可以用“对象.变量名”来调用。对于private变量&#xff0c;即使使用static&#xff0c;也不能用“类.变量名”来调用私有变量。只能通过…

UID走私:一种在线跟踪用户的新技术

©网络研究院 几十年来&#xff0c;广告商和网络追踪者已经能够在用户访问的所有网站上汇总用户信息&#xff0c;主要是通过在用户的浏览器中放置第三方cookies。 两年前&#xff0c;几个优先考虑用户隐私的浏览器开始默认屏蔽所有用户的第三方cookies。 对于那些代表其…

2022年转行编程选哪门语言?这份报告给你答案!

本报告展示了 JetBrains &#xff08;IntelliJ IDEA的开发公司&#xff09;于2021年进行的第五次年度开发者生态系统调查的综合结果。来自 183 个国家或地区的 31,743 名开发者帮助我们绘制了开发者社区版图。 本文将摘录部分个人觉得有价值的报告&#xff0c;从全球视角以国内…