数字位运算操作与算法简单示例

  我们对于位运算可能既陌生又熟悉。知道其运算方法运算过程,但不能运用好它。

  首先,我们还是回顾一下Java中位运算都包含那些操作:

一、与运算(&)

  运算法则:将二进制数进行按位与运算。0&0=0;0&1=0;1&1=1 ;

如:0011 & 0010 = 0010;

  二、或运算(|)

  运算法则:将二进制数进行按位或运算。0|0 =0;1|0 = 1;  1|1=1

  如:0011 & 0010 = 0011;

  三、异或运算(^)

  运算法则:将二进制数进行按位异或。   0^0 = 0; 1^1=0;1^0=1;

  如:0011 ^ 0010 = 0001;

  四、非运算(~)

  运算法则:将二进制数进行按位“取反”操作。 ~0 = 1;~1 = 0;

  如:~0011 = 1100

  五、左移(有符号左移,无符号左移)、右移

  参考上一篇:https://www.cnblogs.com/funmans/p/11179189.html

  第六条:“>>”、“>>>”、“<<”都是什么鬼?

  首先 >> 、>>> 都是Java中的右移操作,“>>”是有符号右移,“>>>”是无符号右移。顾名思义,有符号须得在右移过程中保持符号,所以,有符号右移,若是正数则在高位补0,反之补1,无符号右移不管正负都补0.

特别注意点:对于char、byte、short等类型。在运算时候会先进行int类型的转换。都知道int类型4个字节,32位。所以,如果说右移的位数超过了32,那也是没有用滴。Java采用了取余数的方式来保证右移位数的有效性,即 n>>m相当于 n>>(m%32); 右移相当于做除法操作

“<<”是什么呢?它表示左移操作。那么怎么不见“<<<”这个东西呢?因为左移操作没有有符号与无符号左移。左移操作的过程就是移除最高位,然后在末尾补0就OK。比如:4,二进制100,当然运算时是3位的,

即 0000 0000 0000 0000 0000 0000 0000 0100,如果进行<<3操作,也就是左移三位,去掉头上三个0,后面再补上三个:0000 0000 0000 0000 0000 0000 0010 0000,换算成十进制就是32,所以左移也就是乘法运算。同样的,如果你的左移位数大于32位,其实际左移位数位 对32 取余数的值

好了以上就是基本但位运算法则,接下来我们开始循序渐进的进入第一步:

1、给定一个数我们如何知道这个数M的二进制数的第 N 位是 1 还是 0 呢?

方法:通过与运算以及位移运算,通过将 1 进行左移 n位再进行跟原数据进行与操作。由于我们左移完成的这个数只有第N位是1,其他位置全都是 0 ,所以结果如果等于 0,那么给定数的第N位为0 ,否则为1;

 result = M & (1<<N) == 0 ? 0 : 1 ;

2、给定一个数,我们如何去改变这个数二进制表示中具体某一位的值?

这个首先分两种情况:

将指定数M的二进制数第N为设置为1:

方法:通过或运算以及位移运算,通过将1 进行左移 N位,在进行与 M进行或运算,由于 0|0 = 0;0|1 = 1;所以,低位是不会改变的。

 result = M | (1<<N);

将指定数M的二进制数第N位设置为0:

方法:通过与运算以及位移运算,将1 进行左移N位再取反或-1,然后与上M

result = M & ~(1<<N)  或者  result = M & ((1<<M) -1);


接下来我们进入一个简单的算法阶段:

问题:给定一个整型数组,求这个数组中的第二大元素?

可能很多同学第一眼看到这个有点懵,一想平时都是求数组中的最大的那个,怎么求第二大呢,略一思索后,得到一个方案:我先对这个数组进行排序,然后还不是想取第几大就取第几大。OK,当然排序是来解决这个问题是可以的,但是确实有点问题复杂化了。

实际上我们可能只需要对数组进行一次遍历操作就OK了。(PS:当然肯定还有复杂度更低的方法,这里只是做一个简单举例说明)

    /**
     * 求第二大元素
     * @param data
     * @return
     */
    public int max(int[] data){
        int temp = data[0];
        int max = data[0];
        for (int i = 1; i < data.length; i++) {
            if(data[i]>temp){
                max = temp;
                temp = data[i];
            } else if(data[i] > max && data[i] != temp){
                max = data[i];
            }
        }
        return max;
    }

OK,上面的代码如果只是考虑求取第二大元素,当然也能勉强使用。但是我们写代码肯定是要考虑通用性的,那么如果问题换成求取整型数组中的第N大元素或者第N小问题,又该如何呢?

既然本篇将到了位运算,当然需要用到了,基本思路如下:

1、获取元素最大最小值

2、初始化一个二进制数空间用其每一位来标记数组中的一个数,存在(1)、不存在(0)

3、通过遍历数组进行二进制数每一位的的赋值

4、通过对二进制数求第N个不为0的位数,来获取第N大或第N小

     /**
     * 求第N小
     * @param data
     * @param index
     * @return
     */
    public  int minN(int[] data,int index){
        int max,min;
        max = min = data[0];
        //求最大最小值
        for (int datum : data) {
            max = Math.max(max,datum);
            min = Math.min(min,datum);
        }
        //初始化标记空间
        int bitSpace = 1<<(max - min +1);
        //遍历数组进行空间标记.标记过程就是将bitSpace中的响应位置值设置为1
        for (int datum : data) {
            bitSpace |= 1<<(datum - min);
        }
        //获取bitSpace的index位置值
        for (int i = 0; i < max-min+1; i++) {
            if((bitSpace & 1<<i) != 0){
                index --;
            }
            if(index <= 0){
                return i + min;
            }
        }
        return 0;
    }

以上程序就利用了二进制数据的的位运算来进行编程,利用二进制的 0 1,表示某个数据的存在与不存在。利用位运算进行二进制位的赋值、取值操作。

当然针对上述算法来说,还是又很多缺陷的。

比如:上述不能够针对同一值进行多次标记。

总结:数字的位运算操作在算法中一般的使用场景都是用来作为标记使用。针对上一问题,如果需要针对同一值进行多次标记,则可将bitSpace换成数组(所占空间更大),但思想不变;再比如大名鼎鼎的布隆过滤器,也是标记。

》》》如有不当之处,欢迎批评指正。

    

原文地址:https://www.cnblogs.com/funmans/p/11406553.html

时间: 2024-08-29 00:18:39

数字位运算操作与算法简单示例的相关文章

Node.js操作Redis的简单示例

Redis是一个key-value类型的数据库,而key全部都是字符串,value可以是集合.hash.list等等. Redis是通过MULTI/DISCARD/EXEC/WATCH这4个命令来实现事务功能.对事务,我们必须知道事务安全性是一个非常重要的. 事务提供了一种"将多个命令打包,然后一次性.按顺序执行"的机制,并且在事务执行期间不会中断--意思就是在事务完成之前,客户端的其他命令都是阻塞状态. var redis = require("redis");

文件操作的一个简单示例

在上文介绍了C中文件操作的一些基本函数,下面给一个简单例子,完成从控制台输入一段字符串,然后将其写入文件,然后从文件中读出刚刚写入的内容,代码如下: #include <stdio.h> #include <string.h> #include <stdlib.h> const int LENGTH=80; int main(void){ char mystr[LENGTH]; int lstr=0; int mychar=0; FILE * pfile=NULL; c

HMM的维特比算法简单示例

今天读了一位大牛的关于HMM的技术博客,读完之后,写了一个关于维特比算法的简单示例,用scala和java语言混合编写的.现在上传之. package com.txq.hmm import java.utilimport scala.collection._ /** * HMM维特比算法,根据显示状态链条估计隐式链条 * @param states 隐式states * @param observations 显式states * @param start_probability 初始概率向量

位运算基本概念及简单运用

C语言提供了六种位运算符: &     按位与    |      按位或    ^      按位异或    ~      取反    <<    左移,相当与*2    >>    右移,正数高位补0,负数由计算机决定 循环左移k次 (x<<k) | (x >> (32-k)), 循环右移k次 (x>>k) | (x << (32-k)) 当然常常应为优先级问题而犯错~~~ 优先级及口诀如下 优先级别 运算符 记忆口诀 1

基于DP+位运算的RMQ算法

来源:http://blog.csdn.net/y990041769/article/details/38405063 RMQ算法,是一个快速求区间最值的离线算法,预处理时间复杂度O(n*log(n)),查询O(1),所以是一个很快速的算法,当然这个问题用线段树同样能够解决. 问题:给出n个数ai,让你快速查询某个区间的的最值. 算法分类:DP+位运算 算法分析:这个算法就是基于DP和位运算符,我们用dp[i ][j]表示从第 i 位开始,到第 i + 2^j -1 位的最大值或者最小值. 那么

LeetCode137只出现一次的数字——位运算

题目 题目描述:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现三次.找出那个只出现一次的元素. 说明:你的算法应该具有线性时间的复杂度.你可以不使用额外的空间来实现吗? 思路 题目要求线性复杂度,一般的算法做不到,不难想到用位运算.但怎么进行位运算,比较难想到. b = (b ^ x) & ~a; a = (a ^ x) & ~b; ^ 相当于除去原有元素,添加新元素, a &~  b 相当于从a集合中除去b集合中的所有元素. int len = nums.s

Java 中的按位运算操作

我是木龙小驴,菜鸟一枚,这里发点文字,大家共同学习交流 位运算符无非就是<<  还有 >> 以及>>> 首先很久很久以前,那时候没有操作系统,只有电,计算机运算跟开关点灯差不多,亮代表开,不亮代表关,然后根据电压的高低来进行记录操作. 所以CPU进行所有处理都是二进制的,也就是1 和 0 组成,比如说十进制的1 就是0001 ,2就是0010,3就是0011. 二进制就是逢二进一,当一个数字,或者是字符,或者是什么东西(在计算机运算时都是二进制)如:0010 00

java导入导出excel常用操作小结及简单示例

POI中常用设置EXCEL的操作小结: 操作excel如下 HSSFWorkbook wb = new HSSFWorkbook();  //创建一个webbook,对应一个Excel文件 HSSFSheet sheet = wb.createSheet();    //添加一个sheet,对应Excel文件中的sheet 构造方法可以有参也可以无参wb.createSheet("学生表一") HSSFRow row = sheet.createRow((int) 0);  //she

位运算操作

来源:https://discuss.leetcode.com/topic/50315/a-summary-how-to-use-bit-manipulation-to-solve-problems-easily-and-efficiently/2 WIKI Bit manipulation is the act of algorithmically manipulating bits or other pieces of data shorter than a word. Computer p