首页 > 其他分享 >位运算挑战:通过最少位翻转实现 a OR b == c【逐位处理与右移操作】

位运算挑战:通过最少位翻转实现 a OR b == c【逐位处理与右移操作】

时间:2024-10-25 14:20:36浏览次数:7  
标签:右移 需要 int 复杂度 bit 逐位 翻转

引言

在这篇文章中,我们将详细探讨一个常见的位操作问题给定三个正整数 abc,通过最少的位翻转次数使得 a OR b == c 位操作在计算机科学中十分重要,特别是在涉及高效算法设计、底层优化和嵌入式系统编程时。本篇文章将通过逐步分析这个问题,详细讲解如何利用位运算规则设计解决方案,并且给出 C++ 代码的实现。


题目描述

我们有三个正整数 abc,目标是通过对 ab 的二进制表示进行最少的位翻转操作,使得 a | b == c。我们的任务是计算最少需要多少次位翻转操作,并根据这个问题设计一个有效的算法。

位运算背景知识
  1. 位翻转操作:即将某一位上的 1 变为 0,或将 0 变为 1。这是我们在本题中需要的基本操作。
  2. 按位或运算(OR):按位比较两个数的二进制位,当某一位上至少有一个数为 1 时,结果的该位为 1,否则为 0

例如,假设 a = 2 (010)b = 6 (110),则:

  a = 010
  b = 110
a|b = 110 (即 6)

通过这些基本知识,我们可以清楚地知道,当我们希望使得 a OR b == c 时,两个数在每一位上的或运算结果应与 c 的对应位匹配。


解题思路

为了找到最少的位翻转次数,我们需要逐位比较 abc 的二进制表示。具体来说,我们将从最低位开始逐位处理这三个数的每一位,并根据以下规则决定是否需要翻转:

  1. 如果 c 的当前位是 0

    • 按位或运算的结果要求 ab 在这一位上都必须为 0,否则我们必须翻转 ab 的位。
    • 如果 a 的当前位是 1,则我们需要将其翻转为 0,同理,b 的当前位如果是 1,也需要翻转为 0
  2. 如果 c 的当前位是 1

    • 按位或运算的结果要求 ab 在这一位上 至少 有一个为 1
    • 如果 ab 的当前位都为 0,则我们需要翻转其中一个为 1

通过不断右移 abc 来获取下一位的值,直到所有位都处理完。


位操作与逐位处理

我们可以通过位操作高效地提取整数的每一位,并逐位处理这些位。常用的位操作有:

  • a & 1:提取 a 的最低位。
  • a >>= 1:将 a 右移一位,准备处理下一位。

通过这种逐位提取和移位操作,我们可以遍历所有的二进制位,直到 abc 的所有位都处理完为止。

循环条件

在处理这些二进制数时,循环的终止条件非常关键。我们使用 while (a > 0 || b > 0 || c > 0) 作为循环条件,确保所有位都处理完:

  1. 只要 abc 的某一位还没有处理完,循环就继续进行。这个条件确保了不会有位数没有处理到,即便某些数的位数比其他数少(例如 a 为 3 位,而 b 为 2 位,c 为 4 位),循环也能正常进行。
  2. 使用 a != 0 && b != 0 && c != 0 的话,会导致提前终止,因为一旦某个数的所有位都处理完毕,循环就会结束,但实际上我们需要处理到三者的所有位数。

C++ 代码实现

基于上述思路,以下是一个详细的 C++ 实现:

#include <iostream>
using namespace std;

int minFlips(int a, int b, int c) {
    int flips = 0;  // 用于记录翻转次数
    while (a > 0 || b > 0 || c > 0) {  // 循环直到所有位都处理完
        // 提取a, b, c的最低位
        int a_bit = a & 1;
        int b_bit = b & 1;
        int c_bit = c & 1;
        
        // 如果c的最低位是0,a和b的最低位必须都是0
        if (c_bit == 0) {
            if (a_bit == 1) flips++;  // a当前位为1,需要翻转为0
            if (b_bit == 1) flips++;  // b当前位为1,需要翻转为0
        }
        // 如果c的最低位是1,a和b至少有一个位是1
        else {
            if (a_bit == 0 && b_bit == 0) flips++;  // a和b当前位都为0,需要翻转一个为1
        }
        
        // 右移a, b, c,处理下一位
        a >>= 1;
        b >>= 1;
        c >>= 1;
    }
    return flips;
}

int main() {
    // 示例 1
    int a1 = 2, b1 = 6, c1 = 5;
    cout << "示例 1 输出: " << minFlips(a1, b1, c1) << endl;  // 输出:3
    
    // 示例 2
    int a2 = 4, b2 = 2, c2 = 7;
    cout << "示例 2 输出: " << minFlips(a2, b2, c2) << endl;  // 输出:1
    
    // 示例 3
    int a3 = 1, b3 = 2, c3 = 3;
    cout << "示例 3 输出: " << minFlips(a3, b3, c3) << endl;  // 输出:0
    
    return 0;
}

示例讲解

为了帮助大家理解,我们可以通过具体的示例逐步讲解代码的执行过程。

示例 1:
  • 输入a = 2b = 6c = 5

  • 二进制表示

    • a = 2 => 010
    • b = 6 => 110
    • c = 5 => 101

    逐位比较和操作如下:

    • 最低位a = 0b = 0c = 1,需要翻转 ab 的最低位,使得至少有一个 1,翻转次数增加 1。
    • 第二位a = 1b = 1c = 0,需要将 ab 的第二位都翻转为 0,翻转次数增加 2。
    • 第三位a = 0b = 1c = 1,不需要翻转。

    总共需要翻转 3 次,输出为 3

示例 2:
  • 输入a = 4b = 2c = 7

  • 二进制表示

    • a = 100
    • b = 010
    • c = 111

    逐位比较和操作如下:

    • 最低位a = 0b = 0c = 1,需要翻转 ab 的最低位,使得至少有一个 1,翻转次数增加 1。

    总共需要翻转 1 次,输出为 1

示例 3:
  • 输入a = 1b = 2c = 3
  • 二进制表示
    • a = 001
    • `b = 010

`

  • c = 011

逐位比较和操作如下:

  • 最低位a = 1b = 0c = 1,无需翻转。
  • 第二位a = 0b = 1c = 1,无需翻转。

总共不需要翻转,输出为 0


算法复杂度分析

时间复杂度

在最坏情况下,abc 都可以有最多 O(log(max(a, b, c))) 位,因为一个数的位数与它的二进制表示长度成对数关系。对于每一位,我们执行常数次操作(提取位、比较和翻转),因此总的时间复杂度为 O(log(max(a, b, c)))

空间复杂度

我们只使用了常数级别的额外空间来存储翻转次数和当前位的值,因此空间复杂度为 O(1)

总结

通过对 abc 进行逐位比较,我们能够找出最少的位翻转次数,使得 a | b == c。该算法的时间复杂度为 O(log(max(a, b, c))),空间复杂度为 O(1),非常高效。通过扩展边界条件、复杂度分析和优化方案的讨论,本文不仅详细介绍了基本的位运算方法,也为深入研究和实际应用提供了理论依据。

标签:右移,需要,int,复杂度,bit,逐位,翻转
From: https://blog.csdn.net/qq_22841387/article/details/143226922

相关文章

  • 代码随想录算法训练营第24天(补第13天)|226.翻转二叉树, 101. 对称二叉树,104.二叉树的最
    226.翻转二叉树文章链接:https://programmercarl.com/0226.翻转二叉树.html#算法公开课题目链接:https://leetcode.cn/problems/invert-binary-tree/description/迭代法:这里使用了前序遍历来交换左右孩子节点classSolution{public:TreeNode*invertTree(TreeNode*r......
  • 代码随想录算法训练营第九天|leetcode151.翻转字符串里的单词、卡码网55.右旋字符串
    1leetcode151.翻转字符串里的单词题目链接:151.反转字符串中的单词-力扣(LeetCode)文章链接:代码随想录视频链接:字符串复杂操作拿捏了!|LeetCode:151.翻转字符串里的单词_哔哩哔哩_bilibili自己的思路:直接将空格去掉,然后分割字符串为列表,在列表中进行翻转,不在字符串内部操作,......
  • K个节点翻转链表
    概述起因:leetcode题目25.K个一组翻转链表问题描述给你链表的头节点head,每k个节点一组进行翻转,请你返回修改后的链表。k是一个正整数,它的值小于或等于链表的长度。如果节点总数不是k的整数倍,那么请将最后剩余的节点保持原有顺序。你不能只是单纯的改变节点内部的值......
  • 翻转链表常用写法
    翻转链表常用写法循环写法classSolution{public:ListNode*reverseList(ListNode*head){ListNode*prev=nullptr,*next=nullptr,*now=head;while(now){next=now->next;now->next=prev;prev=......
  • 在盲解卷中,解卷积时滤波器系数翻转,平移与信号相乘再相加。另一种是信号翻转,平移与滤波
    在盲解卷积中,有两种基本的方法来处理信号和滤波器系数:一种是将滤波器系数翻转、平移与信号相乘再相加,另一种是将信号翻转、平移与滤波器系数相乘再相加。这两种方法的区别主要在于处理信号和滤波器的顺序,以及它们对最终结果的影响。1.**滤波器系数翻转(FilterCoefficientFli......
  • 【反转链表】【K个一组翻转链表】两个问题具体思路,文中含多种解法(附完整代码)
    文章目录前言一、如何理解反转链表?二、反转链表1.方法一(递归)方法二(迭代)三、K个一组翻转链表前言本文将围绕【反转链表】问题展开详细论述。采用【递归法】【迭代法】同时,还将进一步升级该问题,讨论【K个一组翻转链表】一、如何理解反转链表?题目链接:[点击......
  • 代码随想录算法训练营day9|●151.翻转字符串里的单词 ●卡码网:55.右旋转字符串 ●28.
    学习资料:https://programmercarl.com/0151.翻转字符串里的单词.html学习记录:151.翻转字符串里的单词(感觉C语言能考虑巧妙解法,而python直接搞就对了)c语言:把字符串整体反转,再用双指针法(slow,fast)依次翻转每一个单词,关键在于如何移除多余空格,用slow指针找到要替换到的位置,用fast......
  • GUI无代码小示例 - 工作流连线实现0/1连续翻转
    效果如下所示,连续点击按钮,输出0、1、0、1...。  步骤新建页面,拖入组件拖入3个组件:数学计算、输入框、按钮。如下所示: 连线和配置按钮点击→函数执行 1减去输入,作为函数输出这样,当首次执行时,默认操作数1将减去输入的1,输出0。 函数输出......
  • 代码随想录算法训练营第八天| 151.翻转字符串里的单词
    151.翻转字符串里的单词文章链接:https://programmercarl.com/0151.翻转字符串里的单词.html#思路视频链接:https://www.bilibili.com/video/BV1uT41177fX/?vd_source=6cb513d59bf1f73f86d4225e9803d47b题目链接:https://leetcode.cn/problems/reverse-words-in-a-string/classS......
  • leetcode926. 将字符串翻转到单调递增
    如果一个二进制字符串,是以一些 0(可能没有 0)后面跟着一些 1(也可能没有 1)的形式组成的,那么该字符串是 单调递增 的。给你一个二进制字符串 s,你可以将任何 0 翻转为 1 或者将 1 翻转为 0 。返回使 s 单调递增的最小翻转次数。示例1:输入:s="00110"输出:1......