算法系列 -- 位运算

Posted 躬匠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法系列 -- 位运算相关的知识,希望对你有一定的参考价值。

目录

一、前言

二、概念了解

三、技巧与实战

3.1常见技巧

3.2 实战

3.2.1 以O(1)的时间复杂度判断一个数是不是2的幂次

3.2.2 计算一个 32 位整数的二进制表示中有多少个 1

3.2.3 将整数A转换为B,需要改变多少个bit位

3.2.4 数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的元素

3.2.5 数组中,只有两个数出现一次,剩下都出现两次,找出出现一次的两个元素。

3.2.6 判断一个数是否是偶数


 

一、前言

位运算在日常编程中也是一个解决问题的利器,而这个利器往往被大多数人所忽略,今天我们就来一起学习一下。

注:下面的代码只提供了php语言的版本。

二、概念了解

运算符解释示例备注
&(与运算)如果两个相应的二进制位都为1,则该位的结果值为1,否则为0

0 & 1 = 0

1 & 1 = 1

0 & x = 0
|(或运算)两个相应的二进制位中只要有一个为1,该位的结果值为1

1 | 0 = 1

0 | 0 = 0

1 | 1 = 1

1 | x = 1
^(异或运算)若参加运算的两个二进制位值相同则为0,否则为1

1 ^ 1 = 0

0 ^ 0 = 0

1 ^ 0 = 1

若x ^ y = z,则x ^ z = y;

y ^ z = x

~(取反运算)

~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1

~0 = 1

~1 = 0

 
 <<(左移)用来将一个数的各二进制位全部左移N位,右补0

<<1 = 3

 
>> (右移)将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数, 高位补03>> = 1 

三、技巧与实战

 

3.1常见技巧

  •  x & (x-1):消去x的最后一位的1
  • a ^b ^b = a :去除重复元素
  • 若x ^y = z;则x ^ z = y;z ^ y = x 

 

3.2 实战

3.2.1 以O(1)的时间复杂度判断一个数是不是2的幂次

分析:如果一个数是2的幂次,则其一定满足下面两个条件

1)该数大于0;

2)该数的二进制表示中只有最高位为1

技巧:使用x & (x -1)消去最高位的1,如果为0则该数是2的幂次,否则不是

代码:

<?php

checkMin(3);
function checkMin($num) 
	if ($num <= 0) 
		return 0;
	
	return intval($num & ($num - 1)) == 0 ? 1 : 0;

3.2.2 计算一个 32 位整数的二进制表示中有多少个 1

技巧:使用x & (x -1)消去最高位的1,计算消去的个数也即1的个数

代码:

<?php
getINum(7); 
function getINum($num) 
 	$count = 0;
 	while ($num> 0) 
 		$count ++;
 		$num = $num & ($num - 1);
 	
 	return $count;

3.2.3 将整数A转换为B,需要改变多少个bit位

分析:将两个整数进行异或操作,最终将问题转换为异或的结果中存在多少个1,也即第二个问题

代码:

<?php
getDiffBit(4,5); 
function getDiffBit($a, $b) 
 	$count = 0;
 	if ($a == $b) 
 		return $count;
 	
 	$diff = $a ^ $b;
 	while ($diff > 0) 
 		$count ++;
 		$diff = $diff & ($diff - 1);
 	
 	return $count;
 

3.2.4 数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的元素

分析:使用 a^b^b 去除重复的元素

<?php
$arr = array(1,2,3,1,2,3,4);
getSingle($arr);

function getSingle($arr) 
	$singleItem = null;
	for($i = 0; $i < count($arr); $i ++) 
		$singleItem ^= $arr[$i];
	
	return $singleItem;


3.2.5 数组中,只有两个数出现一次,剩下都出现两次,找出出现一次的两个元素。

分析:对数组的所有元素进行异或的结果也即是只出现一次两个元素的结果。而这两个元素不想等,所以异或的结果是在某个位子一定存在1。找到最后出现1的位置,并根据这个位置,再次对数组元素进行异或操作,最终便会得到两个元素中的一个;之后再利用x ^ xor = y得出另一个元素

代码:

<?php
$arr =  array(1,1,2,2,4,5);
getSingleEle($arr);

 //因为涉及到了数组的循环,时间复杂度为O(n)
 function getSingleEle($arr) 
 	$xor = null;
 	$a = null;
 	$b = null;
 	//step 1:将数组元素进行异或操作,最终会得到只出现一次的两个元素的异或结果
 	//因为两个元素都只出现了一次,异或的结果是一定存在1元素
 	for ($i = 0; $i < count($arr); $i ++) 
 		$xor ^= $arr[$i]; 
 	
 	
 	//step 2:寻找异或结果中最右侧1所在的位置
 	$position = 0;
 	$temp = $xor;
 	while(($temp & 1) == 0) 
 		$position ++;
 		$temp = $temp >> 1;
 	
 	
 	//step 3:根据step2 中1所在的位置,获取2个元素中position位置为bit为1的元素
 	//最终得到元素x
 	for ($i = 0; $i < count($arr); $i ++) 
 		if (($arr[$i] >> $position) & 1) 
 			$a ^= $arr[$i]; 
 		
 	
 	
 	//step 4: 根据x ^ xor = y公式得到y
 	$b = $a ^ $xor; 
 	echo $a . '_' .$b;
 

3.2.6 判断一个数是否是偶数

分析:如果一个数是偶数,则其二进制位的最低位一定为0。

代码:

<?php
$a = 11;
$bool = ($a & 1) == 0 ? 1 : 0;

4、其他

     其实上面列出的问题还有更优的解决方案,不一定非要用位运算。

     比如,查找一个32位正整数中1的个数,可以参考这篇文章:教你如何正确数1

以上是关于算法系列 -- 位运算的主要内容,如果未能解决你的问题,请参考以下文章

位运算

大数加法

用C++实现高精度加法运算

Javav自学笔记第十五天

Java-位运算

如果只需要结果的低位部分,哪些 2 的补码整数运算可以在不将输入中的高位归零的情况下使用?