算法——位运算

&:按位与。
|  :按位或。
~ :按位取反,带符号位。(注意和!的区别,!只是逻辑取反) 
^ : 异或也叫半加运算:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0。
>> : 表示右移,算数右移,如果该数为正,高位补符。
>>>:表示无符号右移,也叫逻辑右移,高位补零。

小操作:                             



1. 判断奇偶数

  a & 1

2. 交换变量

a ^= b, b ^= a, a ^= b

3. 乘以(除以)2的n次方:左移,右移;取模运算:a % (2 ^ n) 等价于 a & (2 ^ n - 1)

4. 判断一个整数是不是2的次方

如果是2的次方,那么这个数的二进制表示中一定只有一个1。

使用的时候注意:
return ((x&(x - 1)) == 0) && (x != 0);

5. 取int型变量a的第k位(>>的优先级较高,但用的时候还是尽量加)

(a >> k) & 1

6. 将int型变量a的第k位置0

  a = a & ~(1 << k)

7. 将int型变量a的第k位置1

  a = a | (1 << k)

8. int型变量循环左移k次(取int类型为4个字节)

  a = a << k | a >> 32 - k 

9. int型变量a循环右移k次

  a = a >> k | a << 32 - k

15. x 的相反数

  (~x + 1) (跟整型数的补码存储有关)


在网上也看到有说可以用 x | 0 实现 浮点数向下取整,就是floor函数的功能。

位运算是整型数的特权,但这也和语言特性有关,像JAVA,C/C++都是整型和浮点型储存形式完全不一样。

而JavaScript内部,所有数值类型都是按64为浮点数存储,所以也就有了 1.8 | 0 这种操作(当进行位运算的时候,会自动舍弃小数位,转换为32位有符号的整数)。

 例题:                      


  • 一个无序数组里有若干个正整数,范围从1到100,其中99个整数都出现了偶数次,只有一个整数出现了奇数次(比如1,1,2,2,3,3,4,5,5),如何找到这个出现奇数次的整数?

int findFigure(int* array,int length) {
    int result = 0;
    for (int i = 0; i < length; i++) {
        result ^= array[i];
    }
    return result;
}
  • 一个无序数组里有若干个正整数,范围从1到100,其中98个整数都出现了偶数次,只有两个整数出现了奇数次(比如1,1,2,2,3,4,5,5),如何找到这两个出现奇数次的整数?

遍历整个数组,依次做异或运算。结果等于这两个整数的异或结果。这个结果中,至少会有一个二进制位是1(如果都是0,说明两个数相等,和题目不符)。

举个例子,如果最终异或的结果是5,转换成二进制是00000101。此时我们可以选择任意一个是1的二进制位来分析,比如末位。

我们将数组根据末位的不同分成两部分,再将这两部分 分别依次进行异或,最终得到的两个结果就是这两个数。

int findFigure(int* a, int length) {
    int result = 0;
    for (int i = 0; i < length; i++) {
        result ^= a[i];
    }
    return result;
}

void find(int *a,int length) {
    int *x = new int[length], *y = new int[length];
    int result = findFigure(a, length);
    int sign = 0;
    //位运算符优先级高于赋值符号
    while ((result & 1) != 1) {
        result >>= 1;
        sign++;
    }
    int j = 0, k = 0;
    for (int i = 0; i < length; i++) {
        if ((a[i] >> sign) & 1) {
            x[j++] = a[i];
        }
        else {
            y[k++] = a[i];
        }
    }
    cout << findFigure(x, j) << endl;
    cout << findFigure(y, k) << endl;
    delete[]x;
    delete[]y;
}

这题看到的时候,考虑了一下,不能用类似上面的方法,用32位数组记录3*n-1个数,每一位出现的次数,因为有n-1个数是三个,有1个数是两个。那1个数的 二进制 为1的位,在数组中一定是3*k+2,也就是不为3的倍数。
数据量大到一定级别,不能再用cin会超时,这里限8000ms,用cin还是超时了。

#include<iostream>
#include <math.h>
using namespace std;

int sign[32];
int main() {
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 0; i < 3 * n - 1; i++) {
            int t;
            //cin >> t;
            scanf("%d",&t);
            for (int k = 0; k < 32; k++) {
                if ((t >> k & 1)) {
                    sign[k]++;
                }
            }
        }
        int result = 0;
        for (int i = 0; i < 32; i++) {
            if (sign[i] % 3 != 0) {
                result += pow(2, i);
            }
        }
        cout << result << endl;
        memset(sign, 0, sizeof(int) * 32);
    }
    return 0;
}


PS:写的时候,没考虑负数,关键是测试数据里还没有 -_- ,我也没发现。

改了一下,result用 第k位 置1,这样负数也能处理了。

#include<iostream>
#include <math.h>
using namespace std;

int sign[32];
int main() {
    int n;
    cin >> n;
        for (int i = 0; i < 3 * n - 1; i++) {
            int t;
            //cin >> t;
            scanf("%d",&t);
                for (int k = 0; k < 32; k++) {
                    if ((t >> k & 1)) {
                    sign[k]++;
                }
            }
        }
        int result = 0;
            for (int i = 0; i < 32; i++) {
                if (sign[i] % 3 != 0) {            //将result的第i位置1
                    result |=  (1 << i);
                }
            }
            cout << result << endl;
    return 0;
}
 

原文地址:https://www.cnblogs.com/czc1999/p/10151345.html

时间: 2024-10-13 06:53:57

算法——位运算的相关文章

java加密解密算法位运算

一.实例说明 本实例通过位运算的异或运算符 “ ^ ” 把字符串与一个指定的值进行异或运算,从而改变每个字符串中字符的值,这样就可以得到一个加密后的字符串.当把加密后的字符串作为程序输入内容,异或运算会把加密后的字符串还原为原有字符串的值.效果图如下: 二.实现过程 1 package com.itxxz; 2 3 import java.util.Scanner; 4 5 /** 6 * java加密解密算法 7 * 8 * @author 螃蟹 9 * 网站:IT学习者 10 * 网址:ht

Apriori算法-位运算-C语言

原文地址:http://blog.csdn.net/liema2000/article/details/6118423 ///////////////////////////////////////////////////////////////////////////** 文件名称:FAST apriori.cpp * 摘 要:采用位运算提高算法的效率 * 将每一项抽象为数值,通过与运算判断是否存在,abcde为16,8,4,2,1换成2进制,各占一个位置,互相不干扰.局限于字母.换算的比较字

数据结构与算法-位运算

位运算位运算是把数字用二进制表示之后,对每一位上的0或者1的运算.理解位运算的第一步是理解二进制.二进制是指数字每一位都是0或者1,如十进制的2转换为二进制之后是10,而十进制的10转换为二进制之后是1010.在程序员圈子里有一个流传了很久的笑话,说世界上有10种人,一种人知道二进制,另一种人不知道二进制. 下面是几个常见的关于位运算的算法题:基本位运算位运算总共只有5种运算:与&,或|,异或^,左移<<,右移>>.与,或,异或的运算规则: 左移,右移运算规则:左移运算m

[算法]位运算问题之三(实现加减乘除)

题目: 给定32位整数a和b,可正.可负.可0,不能使用算术运算符,可分别实现a和b的加减乘除运算. 加法运算: 无进位相加: a: 001010101 b: 000101111 a^b 001111010 只考虑进位: a   001010101 b   000101111 (a&b)<<1 000001010 把完全不考虑进位的相加值与只与考虑进位的产生值再相加,就是最后的结果.重复这样的过程,直到进位产生的值完全消失,说明所有的过程都加完了. public static int

[算法]位运算问题之一

一.不用额外变量交换两个整数的值 a=a^b; b=a^b; a=a^b; 或者: a=a+b; b=a-b; a=a-b; 二.不同任何比较判断找出两个数中较大 有两种方法,方法一有一定的局限性,a-b的值可能溢出,这样溢出后符号改变,返回结果就不正确. 而方法二对a和b是否异号进行了判断,如果同号,则按照方法一返回,否则直接返回正的数就行. a-b的值如果为0的处理按照大于0处理. //如果n为1,则返回0:如果n为0,则返回1 public static int flip(int n) {

[算法]位运算之二

题目一: 给定一个整形数组arr和一个大于1的整数k.已知arr中只有1个数出现了奇数次,其他的数都出现了偶数次,请返回出现了奇数次的数. 时间复杂度为O(N),额外空间复杂度为O(1). 思路: 整数n与0异或的结果是n,整数n与整数n异或的结果是0.所以先申请一个整形变量,记为eO.把eO和每个数异或(eO=eO^当前数),最后eO的值就是出现了奇数次的那个数. 异或运算满足交换律和结合律. public static void printOddTimesNum1(int[] arr) {

[算法]位运算实现加减法

异或可以实现不考虑进位的加法,相同为0,不同为1 0101 ^ 0001 = 0100 两个数相加不考虑进位从二进制角度上说就是相同为0,不同为1,因为二进制只有两个数1和0 1+1=0 0+1=1 与运算可以实现进位 同为1才为1 0101 & 0001 = 0001 说明最低位有进位 int add(int a, int b) { return b == 0 ? a : add(a ^ b, (a & b) << 1); } 左移一位表示 将进位加到前一位 递归表示要遍历进

算法学习 - 递归与非递归,位运算与乘除法速度比较

递归调用非递归调用 运行时间比较 结论 位运算与乘除法 结论 递归调用/非递归调用 我们都知道,很多算法,都是用递归实现的.当然它们同时也是可以用非递归来实现. 一般我们在对二叉树进行遍历的时候,还有求斐波那契数的时候,递归是非常简单的.代码容易懂,好实现. 但是递归的时候,有一个问题,就是需要压栈.为什么要压栈呢?因为当我在函数内部调用自身的时候,要中断当前的操作继续跳转到下一次的实现,而当前运行的状态要保存起来.所以就把当前状态进行压栈,等到运行到递归条件结束的时候,再弹栈. 所以递归就是需

位运算之——按位与(&amp;)操作——(快速取模算法)

由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快. 按位与(Bitwise AND),运算符号为& a&b 的操作的结果:a.b中对应位同时为1,则对应结果位也为1. 例如: 10010001101000101011001111000 & 111111100000000 --------------------------------------------- 10101100000000 对10101100000000进行右移8位得到的是101011,这就得