位运算算法

预备知识

给一个数n,确定它的二进制表示中的第x位是0还是1

(n>>(x-1)) & 1 == 1
true是1,false是0

将一个数n的二进制表示的第x位修改成1

n |= (1<<(x-1))

将一个数n的二进制表示的第x位修改成0

n &= (~(1<<(x-1)))

提取一个数n二进制表示中最右侧的1

n & -n
-n 相比 n 是将最右侧的1左边的区域全部取反

干掉一个数n二进制表示中最右侧的1

n = n & (n-1)
n-1是将最右侧的1右边的区域(包含1)全部取反

异或(^)运算的运算律

1.两个相同的数异或的结果一定为0。(a^a== 0)
2.任何数与0异或都等于它自己。(a^0== a)
3.异或满足交换律;

判断字符是否唯⼀

算法思路(位图的思想):

利用「位图」的思想,每⼀个「比特位」代表⼀个字符,⼀个 int 类型的变量有 32 位足够表示所有的小写字⺟。比特位里面如果是 0 ,表示这个字符没有出现过。比特位里面的值是 1 表示该字符出现过。
那么我们就可以用⼀个「整数」来充当「哈希表」。

class Solution {
    public boolean isUnique(String astr) {
        //利用鸽巢原理来做优化
        if(astr.length() > 26) return false;

        int bitMap = 0;
        for(int i = 0; i < astr.length(); i++) {
            int x = astr.charAt(i) - 'a';
            //先判断字符是否在位图中
            if(((bitMap >> x) & 1) == 1) return false;
            //把当前字符加入到位图中
            bitMap |= 1 << x;
        }
        return true;
    }
}

丢失的数字

算法思路:
可以用哈希表和高斯求和, 我们这里用位运算

如果我们把数组中的数,以及 [0, n] 中的数全部「异或」在⼀起,那么根据「异或」运算的两个相同的数异或的结果一定为0,最终的异或结果就是缺失的数

class Solution {
    public int missingNumber(int[] nums) {
        int ret = 0;
        for(int i = 0; i < nums.length; i++)   ret ^= nums[i];
        for(int i = 0; i <= nums.length; i++) ret ^= i;
        return ret;
    }
}

两整数之和

算法思路:

  • 异或 ^ 运算本质是「⽆进位加法」;
  • 按位与 & 操作能够得到「进位」;
  • 然后⼀直循环,直到「进位」变成 0 为⽌。
class Solution {
    public int getSum(int a, int b) {
        while(b != 0) {
            int tmp = (a & b) << 1;//需要进位
            a ^= b;//无需进位
            b = tmp;
        }
        return a;
    }
}

递归:

class Solution {
    public int getSum(int a, int b) {
        if(b == 0) return a;
        return getSum(a^b, (a&b)<<1);
    }
}

只出现⼀次的数字

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

只出现⼀次的数字II

算法思路(比特位计数):

设要找的数为 ret 。由于整个数组中,需要找的元素只出现了「⼀次」,其余的数都出现了「三次」,因此我们可以根据所有数的「某⼀个比特位」的总和 %3 的结果,快速定位到 ret 那位「比特位」的值是0 还是 1 。
这样,我们通过 ret 每⼀个比特位上的值,就可以将 ret 还原出来。

class Solution {
    public int singleNumber(int[] nums) {
        int ret = 0;
        for(int i = 0; i < 32; i++) { //依次修改ret中的每一个比特位
            int sum = 0;
            for(int x: nums) //统计 nums 中所有的数的第 i 位的和
                if(((x >> i) & 1) == 1)
                    sum++;

            sum %= 3;
            if(sum == 1) ret |= 1 << i;
        }
        return ret;
    }
}

消失的两个数字

算法思路:
只要是不同的数,异或后肯定有1, 1的那个比特位不相同,所以我们可以把它们分成两组

过程:
1.全部异或在一起
2.找出两个数不同的比特位
3.分组异或

class Solution {
    public int[] missingTwo(int[] nums) {
        //1.先把所有数异或在一起
        int tmp = 0;
        for(int x: nums) tmp ^= x;
        for(int i = 1; i <= nums.length + 2; i++) tmp ^= i;

        //2.找出两个数不同的比特位
        int diff = 0;
        while(true) {
            if(((tmp >> diff) & 1) == 1) break;
            else diff++;
        }

        //3.将数组的数和[1, n + 2] 区间内所有数按照diff位不同,分两类异或
        int[] ret = new int[2];
        for(int x: nums) {
            if(((x >> diff) & 1) == 1) ret[1] ^= x;
            else ret[0] ^= x;
        }
        for(int i = 1; i <= nums.length + 2; i++) {
            if(((i >> diff) & 1) == 1) ret[1] ^= i;
            else ret[0] ^= i;
        }
        return ret;
    }
}

数组变换

先找出最大的数,然后遍历其他数,判断一直乘2能否变成最大那个数

import java.util.Scanner;

public class Main {
    static int maxNums = 0;//记录最大的数
    static boolean check(int x) {
        while (x <= maxNums) {
            x *= 2;
            if (x == maxNums) return false;
        }
        return true;
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = in.nextInt();
            if (arr[i] > maxNums) maxNums = arr[i];
        }
        int i = 0;
        for (; i < n; i++) {
            if (arr[i] == maxNums) continue;
            if (arr[i] % 2 == 0 && check(arr[i])) break;
        }
        if (i == n) System.out.print("YES");
        else System.out.print("NO");
    }
}

位运算,先找到最大的数,遍历其他数。判断最大的数/其他数 是否是2的次方, 判断方式有两种位运算的方式

  • (x & (x-1)) != 0
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] arr = new int[n];
        int maxNums = 0;

        for(int i = 0; i < n; i++) {
            arr[i] = in.nextInt();
            maxNums = Math.max(maxNums , arr[i]);
        }

        boolean flag = true;
        for(int i = 0; i < n; i++) {
            if(maxNums % arr[i] != 0) {
                flag = false;
                break;
            }
            int x = maxNums / arr[i];
            if((x & (x-1)) != 0) {
                flag = false;
                break;
            }
        }
        if(flag) System.out.println("YES");
        else System.out.println("NO");
    }
}
  • (x - (x & -x)) != 0
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] arr = new int[n];
        int maxNums = 0;

        for(int i = 0; i < n; i++) {
            arr[i] = in.nextInt();
            maxNums = Math.max(maxNums , arr[i]);
        }

        boolean flag = true;
        for(int i = 0; i < n; i++) {
            if(maxNums % arr[i] != 0) {
                flag = false;
                break;
            }
            int x = maxNums / arr[i];
            if((x - (x & -x)) != 0) {
                flag = false;
                break;
            }
        }
        if(flag) System.out.println("YES");
        else System.out.println("NO");
    }
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
八皇后问题是一个经典的回溯算法问题,但是使用位运算可以使得算法更为高效。具体来说,我们可以使用一个整数变量来表示每一行中哪些位置已经被占用,然后通过位运算来判断某个位置是否可以放置皇后。 具体的算法步骤如下: 1. 定义一个长度为8的数组board,用于存储每一行中皇后所在的列号。 2. 定义三个整数变量:col、ld、rd,分别表示列、左对角线、右对角线上已经被占用的位置。 3. 从第0行开始,依次遍历每一行。 4. 对于当前行i,遍历该行中的每一列j。 5. 判断当前位置是否可以放置皇后,即判断col、ld、rd三个变量中是否有与当前位置冲突的位置。 6. 如果当前位置可以放置皇后,则将该位置的列号存入board数组中,并更新col、ld、rd三个变量。 7. 递归处理下一行。 8. 如果已经处理完了第7行,则说明找到了一组解,输出结果。 9. 回溯到上一行,尝试其他的列。 10. 如果所有的列都尝试完了,仍然没有找到解,则回溯到上一行。 下面是使用位运算实现八皇后问题的Python代码: ```python def solveNQueens(): def dfs(row, col, ld, rd, path, res): if row == n: res.append(path) return for i in range(n): if not (col & 1 << i or ld & 1 << row + i or rd & 1 << row - i + n - 1): dfs(row + 1, col | 1 << i, ld | 1 << row + i, rd | 1 << row - i + n - 1, path + [i], res) n = 8 res = [] dfs(0, 0, 0, 0, [], res) return res print(solveNQueens()) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值