位运算基础知识

位运算基础知识

1.逻辑运算符

按位与 &
按位或 |
异或 ^
按位取反 ~
  1. 位运算符
左移 <<
右移 >>

详解

1. 位与 
两个数的二进制均为1则为1,否则为0
2. 位或
两个数的二进制对应中若有1则为1,否则为0
异或
两个数的二进制不同为1,否则为0
按位取反
依次取一个数的相反的二进制数
左移
将1个数的所有二进制位数向左偏移,末尾补零
右移
将一个数的二进制向右偏移,若该数为负数,高位补1,否则补0

例子

2的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。

分析:

如果 n < 0 则它必然不是2的幂,如果这个数大于零,则它的二进制数(比如1000)一定是1后面跟上若干个零,如果这个二进制数减一(0111),它们进行按位与运算,则结果一定为0.
class Solution {
public:
    bool isPowerOfTwo(int n) {
        return n > 0 && (n & (n-1)) == 0;
    }
};

4的幂

给定一个整数,写一个函数来判断它是否是 4 的幂次方。如果是,返回 true ;否则,返回 false 。

分析:

数论结论:
2的偶数幂 mod 3 = 1
2的奇数幂 mod 3 = 2
class Solution {
public:
    bool isPowerOfFour(int n) {
         return n > 0 && (n & (n-1)) == 0 && n % 3 == 1;
    }
};

位1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。
任何一个二进制数由0或1组成,比如1000 减一为 0111,两者位与运算,结果为0,则消掉一个1,通过循环,不断将n与(n-1)位与,若结果为0则出循环。
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int t = 0;
        while (n) {
           n =  n&(n-1);
           t++; // 每次位与消除一个1,次数加一
        }
        return t;
    }
};

交换数字

编写一个函数,不用临时变量,交换a 与b 的值
对于异或而言,相同的数异或为0,任何一个数与0异或为仍为它本身
a = a ^ b;
b = a ^ b; // 即 b = a ^ b ^ b , b = a ^ 0 = a 此时 b1 = a
a = a ^ b;// 即 a = a ^ b ^ b1 , a = a ^ b ^ a = b;
int * swapNumbers(int* a, int aSize, int * returnSize) {
    a[0] = a[0] ^ a[1];
    a[1] = a[0] ^ a[1];
    a[0] = a[0] ^ a[1];
    *returnSize = 2;
    return a;
}

只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
异或的性质:
相同的数字异或为0
任何数与0异或结果为他本身
满足 交换律 1314 ^ 520 = 520 ^ 1314
满足 结合律 (1314 ^ 520) ^ 521 = 1314 ^ (520 ^ 521)
根据异或的性质,我们可以将所有的数字均异或一次,相同的数字异或后最终等于零,只出现一次的数字与零异或等于其本身,因此,可以找出只出现一次的那个数
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) {
            sum = sum ^ nums[i];
        }
        return sum;
    }
};

汉明距离

两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x 和 y,计算并返回它们之间的汉明距离。
先将这两个整数进行异或运算,得出它们二进制位不同的总数目(1的个数)
然后再统计异或结果中二进制1的个数
class Solution {
public:
    int hammingDistance(int x, int y) {
        int n = (x ^ y);
        int t = 0;
        while (n) {
            n &= (n-1);
            t++;
        }
        return t;
    }
};

交替位二进制数

给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。
对于一个二进制数,如果相邻两位为00,或11则相同,否则为不同,00的十进制为0,11的十进制为3,则可以通过循环,此次位与3,如果为3则找到11相邻的二进制数,或者如果为0则找到00相邻的二进制的数,直接返回false,循环结束,返回true
class Solution {
public:
    bool hasAlternatingBits(int n) {
        while(n) {
            if ((n&3) == 0 || (n&3) == 3) return false;
            n >>= 1;
        }
        return true;
    }
};

找出所有子集的异或总和再求和

一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果;如果数组为 空 ,则异或总和为 0 。
例如,数组 [2,5,6] 的 异或总和 为 2 XOR 5 XOR 6 = 1 。
给你一个数组 nums ,请你求出 nums 中每个 子集 的 异或总和 ,计算并返回这些值相加之 和 。
注意:在本题中,元素 相同 的不同子集应 多次 计数。
数组 a 是数组 b 的一个 子集 的前提条件是:从 b 删除几个(也可能不删除)元素能够得到 a 。
对应一个数组[5,2,0]它的子集有[],[0],[2],[5],[0,2],[0,5],[2,5],[5,2,0];
一共八个即2的3次方,刚好可以用二进制表示。我们可以用数组的长度的二进制表示子集,0代表没有这个数,1代表有这个数,则上面的子集依次对应的二进制数为[000],[001],[010],[100],[011],[101],[110],[111]。最低位的二进制代表0的状态,中间的二进制代表2的状态,最高位的二进制代表5的状态。
然后通过(i & (1 << j)公式来判断i 这个数字的二进制表示中从低到高第j位是否为1,把是1的位对应的数字进行异位。
class Solution {
public:
    int subsetXORSum(vector<int>& nums) {
        int sum = 0;
        for (int i = 0; i < (1 << nums.size()); i++) { // 也可以i从1开始,因为0&任何数均为0
            int ret = 0;
            for (int j = 0; j < nums.size(); j++) {
                if (i & (1 << j)) ret ^= nums[j];
            }
            sum += ret;
        }
        return sum;
    }
};

两整数之和

给你两个整数 a 和 b ,不使用 运算符 + 和 -,计算并返回两整数之和。
^运算是相同的数为0,不同的数为1,若不考虑进位,则异或可以理解为加法,但是如果考虑进位,则将两个1进行按位与运算,再进行移位。
class Solution {
public:
    int getSum(int a, int b) {
        return b == 0 ? a : getSum(a^b, (unsigned int)(a & b) << 1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-Gaojs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值