给定一个正整型数值,如何获取该数下一个2的n次幂值

问题解读

题目的意思就是获取某个数的下一个2的n次幂,比如说输入5,2的2次幂是4,2的3次幂是8,所以应该输出 8

咦,怎么有一股似曾相识的感觉呢?如果有研究过HashMap源码的同学应该会知道,HashMap底层数组长度不就是2的n次方吗,那HashMap是不是就会有这道题的答案呢?

答案是肯定的,HashMap中确实就有这个方法,而且实现上非常巧妙与简洁,下面让我们来看看这道题的标准答案吧!

首先我们来看看他的源码

位于HashMap中的

tableSizeFor(int cap)
复制代码

方法中

 static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
复制代码

对位运算不是特别熟悉的同学,初次看到这段代码可能会有点懵,下面让我们来慢慢解读一下这段代码,解读之前,我们需要先了解一下位运算是什么东西,以及上面代码出现的位运算符是干什么的。

十进制与二进制

在生活中,我们所看到的数字几乎全都是十进制的数字,但是在计算机底层中存储的以及它能识别的数字都是二进制的,而位运算就是直接对整数在内存中的二进制位进行操作的运算,对比十进制的运算,它的速度更加快。

二进制与十进制可以相互转化,例如:

十进制     二进制
2           10
3           11
4           100
5           101
6           110
7           111
8           1000
复制代码

具体转换公式可访问 百度百科(点我)

位运算符

位运算符就是直接操作二进制数的运算符

分别有:按位与、按位或、按位异或、按位取反、左移、带符号右移、无符号右移 具体释义可参考这篇文章

这里解释一下上面代码出现的两个位操作

  1. |=

|=的意思就是或等于,跟+=差不多

a|=b 等价于 a=a|b
复制代码

| 这个符号则是按位或:将两数转换成二进制,对比同位上元素,同位上只要有一个1或者全都是1,那结果就是1,否则就是0

举个例子:

int a = 8 | 7
转换成二进制
8:   1 0 0 0 
7:     1 1 1
-------------
结果:1 1 1 1
而1111转换成十进制的话那就是15
所以a=15
复制代码
  1. >>> 无符号右移

将数值转换成二进制后向右移动n位

例如:

int a  = 8 >>> 2
8转换为二进制就是 
1 0 0 0
向右移动两位变成
    1 0
所以a = 2
复制代码

原代码解析

static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
复制代码

通过对十进制进行二进制转换后,我们可以发现一个规律,那就是2的n次幂的二进制数,除了首位是1,其他位都是0,比如说2的1次幂是10、2的2次幂是100、2的3次幂是1000, 所以

假如我们要得到一个数的下一个2的n次幂,那么需要将这个数转换成2进制之后,把它的所有位上的数值都变成1,最后加1,就能够得到这个数的下一个2的n次幂了

举个例子,我们要获取6的下一个n次幂

原数:6
1.转换成二进制:110
2.把它的所有位变成1:111
3.加一使其进位:1000
4.转换成十进制:8
复制代码

所以就上面源码的步骤就可以解析为

     //把数无符号右移一位后,与原数按位或,这样可以保证这个数的最高位与次高位是1
       n |= n >>> 1;
       // 由于这个数的最高位与次高位已经是1了,所以这次向右移动两位并按位或,使前四位都是1
        n |= n >>> 2;
        // 使从最高位数,前八位变成1
        n |= n >>> 4;
        // 同理,使前十六位变成1
        n |= n >>> 8;
        // 因为int最高只有32位 ,所以到这里就可以确定这个数的所有位都是1了,
        n |= n >>> 16;
        // 加一则所有1位向前进一位变成0,即是2的n次幂
        n+1
复制代码

至此,这个问题的就已经解读得差不多了

问题引申

HashMap里面数组的长度为什么总是2的n次幂?

因为HashMap是一个用来存储key-value的一个集合,它的底层是数组+链表+红黑树。

当向HashMap中添加元素的时候,它需要对key进行hash计算得到hash,然后根据数组的长度对hash进行取模,得到这个元素应该存放在这个数组的哪个位置上,也就是说要进行 hash % n 这个操作,而这个操作是比较消耗性能的,所以我们能不能将这个操作转换成位运算,以此避免无谓的性能损耗呢?

答案是有的:当被余数是2的n次幂时

(n-1)& hash等价于hash % n

具体解析可看这篇文章 由HashMap哈希算法引出的求余%和与运算&转换问题

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用以下步骤来给定一个整数,将其按照从小到大的顺序输出它的因数: 1. 首先,判断给定整数n是否小于等于0。如果是,则它没有因数,直接输出空序列。 2. 对于整数n,从1到n-1遍历每个整数i,如果n除以i余数为0,则i是n的一个因数,将其加入到一个列表中。 3. 遍历完所有可能的因数后,将列表中的数按照从小到大的顺序排列,并输出即可。 以下是一个Python实现的示例代码: ``` def get_factors(n): if n <= 0: return [] factors = [] for i in range(1, n+1): if n % i == 0: factors.append(i) return factors n = 20 factors = get_factors(n) print(factors) ``` 这段代码的输出结果将是 `[1, 2, 4, 5, 10, 20]`,它是20的所有因数按照从小到大排列的结果。 ### 回答2: 给定一个数,倒序输出这个整数可以通过以下步骤实现: 1. 通过取模运算和整除运算,将整数从个位开始逐位拆解。 2. 每次取得的余数即为当前位上的数字。 3. 将每次得到的余数依次相加排列,即可得到倒序输出的整数。 举例说明: 假设输入的数为123456。 首先,取模运算:123456 % 10 = 6,余数为6。 然后,整除运算:123456 // 10 = 12345,结果为12345。 接着,再次取模运算:12345 % 10 = 5,余数为5。 再次整除运算: 12345 // 10 = 1234,结果为1234。 重复以上步骤,直到将所有位数的数字都取出来。 最终,将每次得到的余数相加排列,即为倒序输出的整数:654321。 因此,对于输入的数123456,倒序输出为654321。 ### 回答3: 以字符串的形式操作该整数,先转换成字符串,然后将字符串反转输出即可。 算法步骤如下: 1. 将整数转换成字符串类,可以使用内置函数str()来进行转换。 2. 将转换后的字符串进行反转操作,例如可以使用切片操作[::-1]来实现。 3. 输出反转后的字符串。 代码如下: ```python def reverse_number(num): num_str = str(num) reversed_num_str = num_str[::-1] return reversed_num_str # 测试 num = 12345 reversed_num = reverse_number(num) print(reversed_num) ``` 输出结果为:"54321"。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值