【Leetcode】[190] 颠倒二进制位

【Leetcode】[190] 颠倒二进制位

Author: Xin Pan

Date: 2022.3.13


题目

原题链接

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

解法

考虑使用位运算来做,因为这32位0 1都很明显了。

示例 1:

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

答案1 位移动+分治

怎么样,看到这个解法惊不惊喜意不意外。说真的,根本没想到分治。看了题解发现用了分治之后效果还是挺明显的。官方分治的题解是如下。

执行用时: 0 ms 内存消耗: 5.7 MB

class Solution
	{
	private:
		const uint32_t M1 = 0x55555555; // 01010101010101010101010101010101
		const uint32_t M2 = 0x33333333; // 00110011001100110011001100110011
		const uint32_t M4 = 0x0f0f0f0f; // 00001111000011110000111100001111
		const uint32_t M8 = 0x00ff00ff; // 00000000111111110000000011111111
	public:
		uint32_t reverseBits(uint32_t n)
		{
			n = n >> 1 & M1 | (n & M1) << 1; // 奇偶交换
			n = n >> 2 & M2 | (n & M2) << 2; // 每2位交换
			n = n >> 4 & M4 | (n & M4) << 4; // 每4位交换
			n = n >> 8 & M8 | (n & M8) << 8; // 每8位交换
			return n >> 16 | n << 16;
		}
	};

我用代码解释下其中的每行的含义。因为位的运算比较抽象,但是效率高。

#include <iostream>
#include <bitset>
using namespace std;

int main()
{
	uint32_t M1 = 0x55555555;
	uint32_t n = 43261596;

	// 运算符优先级 << >> 高于 按位与(&) 高于 按位异或(^) 高于 按位或(|)
	// 我自己记不住 按位与(&) 按位异或(^)  按位或(|) 三个运算什么时候结果是0,什么时候结果是1。为此我变了一个顺口溜
	// &:全1才是1;
	// |: 有1就是1;
	// ^: 不同才是1。
	// 记住顺口溜我们看下边的代码
	cout << "输入的二进制表示\t\t" << bitset<sizeof(n) * 8>(n) << endl;//int占4字节,一个字节8位,最终输出的是32个0或1
	cout << "输入>>1\t\t\t\t" << bitset<sizeof(n) * 8>(n >> 1) << endl;//int占4字节,一个字节8位,最终输出的是32个0或1
	cout << "M1(奇偶掩膜)\t\t\t" << bitset<sizeof(M1) * 8>(M1) << endl;
	cout << "输入>>1&M1\t\t\t" << bitset<sizeof(n) * 8>(n >> 1 & M1) << endl;
	cout << "输入&M1再<<1\t\t\t" << bitset<sizeof(n) * 8>((n & M1) << 1) << endl;
	cout << "输入>>1&M1 | 输入&M1再<<1\t" << bitset<sizeof(n) * 8>(n >> 1 & M1 | (n&M1) << 1) << endl << endl;


	cout << "输入的二进制表示\t\t" << bitset<sizeof(n) * 8>(n << 1) << endl;
	cout << "M1(奇偶掩膜)\t\t\t" << bitset<sizeof(M1) * 8>(M1) << endl;
	cout << "输入&M1\t\t\t\t" << bitset<sizeof(n) * 8>(n & M1) << endl;
	cout << "输入&M1<<1\t\t\t" << bitset<sizeof(n) * 8>((n & M1) << 1) << endl;
	cout << "输入<<1再&M1\t\t\t" << bitset<sizeof(n) * 8>(n << 1 & M1) << endl;

	system("pause");
	return 0;
}

结果如下:

在这里插入图片描述

  1. &M1这个掩膜相当于固定提取当前奇数位置的值;

那么对于上半部分来说。首先将偶数位置移动到奇数位置,在和M1取按位与计算,就是将输入的偶数信息拿过来了。为什么右移一位不能说是奇数位到偶数位。因为第一位和最后一位会在移动(左移或者右移)过程中直接被抹掉。所以原值等于变了。故而我们不说奇数变到了偶数位置。

那么怎么将原先奇数位置转移到偶数位置呢,就是直接做按位与M1,再左移就可以了。现在我们有了各自移动好位置的奇数和偶数部分,下边就是将它们合并起来,那怎么做?直接做按位或就可以了。

这就完成了奇偶位数的互换工作了。因为采用分支算法,也就是将32位对半分,直到最小的维度相邻的两个值。

反过来我们计算了相邻(奇偶的互换),同理计算每2 4 8 16互换就完成任务了。

答案2 位移动

直接解法,一位位移动

执行用时: 4 ms 内存消耗: 5.9 MB

	class Solution
	{
	public:
		uint32_t reverseBits(uint32_t n)
		{
			uint32_t nret = 0;
			for (int i = 0; n > 0, i < 32; i++)
			{
				nret |= (n & 1) << (31 - i);
				n >>= 1;
			}
			return nret;
		}
	};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值