加载中...
返回

难题本 | LeetCode190. 颠倒二进制位

题目链接:https://leetcode-cn.com/problems/reverse-bits/

一道简单题,却能引出一个无比骚包的操作。此法前所未见,独自亦难想出,因此大抵也算是难的。

题目

颠倒给定的 32 位无符号整数的二进制位。

示例1

  • 输入: 00000010100101000001111010011100

  • 输出: 00111001011110000010100101000000

  • 解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。

示例2

  • 输入: 11111111111111111111111111111101
  • 输出: 10111111111111111111111111111111

分析

易得简单暴力的32次循环法,在此不表,详见AC代码1。唯有一点值得注意:先加最低位再将 res 移位的时候,只能移动 31 次,否则最高位将溢出。因此,AC代码1中只取31次循环,而将最后一位置于循环外。


对于一个首尾对换的操作,我们可以使用分治的思路。

考虑对于最大的32位二进制,将前16位与后16位对换。

考虑高低的16位,将每一部分中的高8位与低8位对换。

如此进行······

那么,我们如何做到这种形式的对换呢?

不妨以8位二进制为例,对于一个八位二进制数 1011 0011 ,我们首先需要将其高低四位进行对换。示意如下:

对于一个二进制数,我们可以很简单地使用如下办法取得它的高低四位:

highBits = 0x10110011 & 0x11110000 = 0x10110011 & 0xf0

lowBits = 0x10110011 & 0x00001111 = 0x10110011 & 0x0f

然后根据图中所示,高四位移至低四位,低四位移至高四位,即分别采取右移和左移的办法:

lowerPart = highBits >> 4

upperPart = lowBits << 4

最后一步就非常简单了,两部分按位取或即可:

final = lowerPart | upperPart

如此一来,我们实现了高低四位的对换。

紧接着,对于两部分4位来说,每4位中需要进行高低二位的对换。示意如下:

在这里,我们的两部分分别可以表示如下:

final = higherPart | lowerPart = (origin & 0x33 << 2) | (origin & 0xcc >> 2)

最后,在每一个二位中进行高低一位的对换,示意图略。

8位如此解决,16位当然也可,32位亦然。

AC代码1

class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        uint32_t res = 0;
        // 注意此循环的条件
        for (int i = 0; i < 31; i++)
        {
            res += (n & 1);
            res = res << 1;
            n = n >> 1;
        }
        res += (n & 1);
        
        return res;
    }
};

AC代码2

class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        // 注意!由于移位操作的优先级高于按位与,需要在与操作部分添加括号
        n = ((n & 0xffff0000) >> 16) | ((n & 0x0000ffff) << 16);
        n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8);
        n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4);
        n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2);
        n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1);

        return n;
    }
};

参考资料

[1] 负雪明烛.「循环」与「分治」解法[EB/OL].2021-03-29

https://leetcode-cn.com/problems/reverse-bits/solution/fu-xue-ming-zhu-xun-huan-yu-fen-zhi-jie-hoakf/

10 comments
Anonymous
Markdown is supported
@mpv945
mpv945commentedover 2 years ago

添加图片,如果使用外部图床的http链接 。图片无法点击放大,你那边怎么解决的?

@SGS4ever
SGS4evercommentedover 2 years ago

@mpv945
添加图片,如果使用外部图床的http链接 。图片无法点击放大,你那边怎么解决的?

我的博客没有使用图床,所以没办法帮到你~

@Celetherin
Celetherincommentedover 2 years ago

您好,我也是使用的stack主题,我在照着您的方法添加返回顶部按钮时,遇到了按钮虽然出现、也能够点击,但无法实现实际上的返回顶部功能的问题,我没有任何的代码知识,不知道您有没有解决方法?
另外,也是想提醒一下其他需要这篇教程的朋友,最新版的stack主题,添加返回按钮的组件应该在layouts/partials/sidebar/right.html, 在layouts/_default/single.html中添加代码会导致出现两个右边栏。

@jsjcjsjc
jsjcjsjccommentedover 2 years ago

请教一下博主,如何优雅的给stack主题添加广告哈?
我只想在左或者右侧边栏底部,或者每篇文章底部添加一个小小的广告,但是默认似乎的满屏广告哈~~
感谢

@SGS4ever
SGS4evercommentedover 2 years ago
@ClimbingMouse
ClimbingMousecommentedalmost 2 years ago

你好,按照你的方法设置页面载入动画,这个动画不会停止咋办啊

@46fafa
46fafacommentedalmost 2 years ago

博主你好,请问一下主页布局修改哪里的代码如何作用于整个网页,我发现修改后的布局只存在主页和前两篇文章,其他部分还是没修改的样子

@4kohakunushi
4kohakunushicommentedover 1 year ago

你好,关于左侧栏图标高亮我这里存在一些问题想请教你。我取消了原本主页直接抓取post的内容在中间显示的版块,这个部分改成了其他东西,与此同时新增了一个抓取post信息的与links、search等目录并列的一个目录,现在的问题是这些部分虽然都能正常显示,但是对应的抓取post的那个目录无法选中以后高亮,应该修改增加什么才能让它也可以选中后高亮呢?

@SGS4ever
SGS4evercommentedover 1 year ago

首先我只能基于本文使用的Stack版本来尝试解答,因为没看过当前的Stack主题的代码~
我重新翻了下此前写的关于高亮的内容,理论上只要你的post页面的标题在menu配置中即可高亮。如果post页面是你站点的根路径,那应该可以参考我的文章里写的方法,修改下active的触发逻辑~

@4kohakunushi
你好,关于左侧栏图标高亮我这里存在一些问题想请教你。我取消了原本主页直接抓取post的内容在中间显示的版块,这个部分改成了其他东西,与此同时新增了一个抓取post信息的与links、search等目录并列的一个目录,现在的问题是这些部分虽然都能正常显示,但是对应的抓取post的那个目录无法选中以后高亮,应该修改增加什么才能让它也可以选中后高亮呢?

@sansan-cc
sansan-cccommentedabout 1 month ago

感谢博主的建站帖子,有很大的帮助。

有朋自远方来,不亦说乎?