likes
comments
collection
share

算法技巧-位操作

作者站长头像
站长
· 阅读数 51

位操作(Bitwise operations)是计算机编程中用于直接操作二进制位的操作。这些操作可以在位级别上对整数类型的数据执行逻辑运算或位移操作。常见的位操作包括按位与(AND)、按位或(OR)、按位异或(XOR)、按位取反(NOT)和位移(左移和右移)

位操作符和其含义

  1. 按位与(AND):使用符号 "&" 表示,将两个操作数的对应位进行逻辑与运算。只有当两个操作数的对应位都为1时,结果位才为1,否则为0。
  2. 按位或(OR):使用符号 "|" 表示,将两个操作数的对应位进行逻辑或运算。只要两个操作数的对应位中至少有一个为1,结果位就为1,否则为0。
  3. 按位异或(XOR):使用符号 "^" 表示,将两个操作数的对应位进行逻辑异或运算。当两个操作数的对应位不同时,结果位为1,否则为0。
  4. 按位取反(NOT):使用符号 "~" 表示,对操作数的每个位进行逻辑取反运算。将0变为1,将1变为0。
  5. 左移(左位移):使用符号 "<<" 表示,将操作数的二进制表示向左移动指定的位数。左移操作会在右侧填充0,并将左侧位移出的位丢弃。
  6. 右移(右位移):使用符号 ">>" 表示,将操作数的二进制表示向右移动指定的位数。右移操作会在左侧填充0或符号位,并将右侧位移出的位丢弃。

经典算法

异或相关

异或运算有以下三个性质:

  • 任何数和 0 做异或运算,结果仍然是原来的数,即 a⊕0=a
  • 任何数和其自身做异或运算,结果是 0,即a⊕a=0
  • 异或运算满足交换律和结合律,即 a⊕b⊕a=b⊕a⊕a=b⊕(a⊕a)=b⊕0=b

算法技巧-位操作

只出现一次的数字

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

class Solution {
    public int singleNumber(int[] nums) {
        int single = 0;
        for (int num : nums) {
            single ^= num;
        }
        return single;
    }
}

只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。 请你找出并返回那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。

由于数组中的元素都在 int(即 32 位整数)范围内,因此我们可以依次计算答案的每一个二进制位是 0 还是 1 具体地,考虑答案的第 i 个二进制位(i 从 0 开始编号),它可能为 0 或 1。对于数组中非答案的元素,每一个元素都出现了 3 次,对应着第 i 个二进制位的 3 个 0 或 3 个 1,无论是哪一种情况,它们的和都是 3 的倍数(即和为 0 或 3)。 因此:答案的第 i 个二进制位就是数组中所有元素的第 i 个二进制位之和除以 3 的余数

class Solution {
    public int singleNumber(int[] nums) {
        int ans = 0;
        for (int i = 0; i < 32; ++i) {
            int total = 0;
            for (int num: nums) {
                total += ((num >> i) & 1);
            }
            if (total % 3 != 0) {
                ans |= (1 << i);
            }
        }
        return ans;
    }
}

类似解法的题目还有 数组中出现次数超过一半的数字

数组中只出现一次的两个数字

一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 数据范围:数组长度 2≤n≤1000,数组中每个数的大小 0<val≤1000000 要求:空间复杂度 O(1),时间复杂度 O(n) 提示:输出时按非降序排列。

public int[] FindNumsAppearOnce (int[] array) {

        // 先将全部数进行异或运算,得出最终结果
        int tmp = 0;
        for(int num: array){
            tmp ^= num;
        }

        // 找到那个可以充当分组去进行与运算的数
        // 从最低位开始找起
        int mask = 1;
        while((tmp&mask) == 0){
            mask <<= 1;
        }

        // 进行分组,分成两组,转换为两组 求出现一次的数字 去求
        int a = 0;
        int b = 0;
        for(int num:array){
            if((num&mask) == 0){
                a ^= num;
            }else{
                b ^= num;
            }
        }
        // 因为题目要求小的数放前面,所以这一做个判断
        if(a > b){
            int c = a;
            a = b;
            b = c;
        }
        return new int[]{a,b};
    }

其他操作

一个十进制数的二进制形式中1的个数

与运算 利用与运算0&1=0,不断将一个非零数不断将二进制形式中的1置于0

int get_one_num(int n){
 int res = 0;
 while(n){
    n = n&(n-1);
    res++;
 }
 return res;
}

判断一个数是否为2的幂

与运算 利用与运算中,2&1=0,4&3=0,利用2的幂-1与2的幂的与运算结果为0,2的幂的二进制形式一定只有一位为1,其他为0,而他的减一形式一定是这一位为0,其他为1.

boolean istwores(int n){
     return n&(n-1)==0?True:False;
}
转载自:https://juejin.cn/post/7283027035965194303
评论
请登录