位运算相关知识点(例子:找重复的数,找出落单的数,查找二进制数1的个数,一个整数是不是2的整数次方,将整数的奇偶位交换,0-1间浮点数的二进制表示,其他数出现k次,只有一个数出现一次,找出它)

  1. 判断奇偶数
    可以这样:就是将这个数和1进行与运算,如果结果是1就是奇数,0就是偶数
    因为1的二进制最低位是1,其他位都是0,与运算就可以将其他位都变成0,最低位是0还是1决定于这个数的最低位是0还是1

  2. 获取二进制位是0还是1
    可以将1左移到该位,然后和这个数进行与运算,将运算的结果再右移回来,看结果是多少就是多少了

  3. 不用判断语句,求整数的绝对值
    方法一:

/*
		 * 当数字的最高位是1时(即负数时)就是将这个数乘与-1,这样就变成正数
		 * 当数字时正数时乘与1,不变
		 */
		return num * (1 - ((num >>> 31) << 1));

方法二:

		//当num为正时: num >> 31=0;num ^ (num >> 31))=num
		//当num为负时: num >> 31=-1;num ^ (num >> 31))=num的反码,再加上1就是原码了
	    return (num ^ (num >> 31)) - (num>> 31); 

  1. 交换两个整数变量的值,要求空间复杂度是0
int a=10;
int b=20;
a=a^b;
b=a^b;
a=a^b;

end(补充小知识):

  1. 对于int型的,1 <<35和1<<3的结果是相同的.对于左边的操作数是long型的,最大是64位,所以对右边的操作数模64
  2. 对于异或的性质:
    ①可以理解成不进位的加法:1+1=0;0+0=0;1+0=1
    ②交换律:可以任意交换运算因子的位置,结果不变
    ③结合律:(a^b) ^c=a ^ (b ^c)
    ④对于任何数x,都有x^x=0,x ^0=x, 同自己求异或为0,同0求异或为自己
    ⑤自反性: A^ B ^ B=A^0=A, 连续和同一个因子做异或运算,最终结果为自己

下面做一个小题:

	@Test
	public void test1()
	{
		/*
		 * 1-1000这些1000个数放在含有1001个元素的数组里面,
		 * 只有唯一的一个元素重复,其他均只出现一次.设计一个算法,
		 * 每一个元素只能访问一次,不能使用辅助空间,将这个重复的数找出来
		 */
		//先构造这样子的数组
		int N=11;
		int[] a=new int[N];
		for (int i = 0; i < a.length-1; i++)
		{
			a[i]=i+1;
		}
		//随机产生0到10的随机数和0-10的随机下标
		int temp=(int)(Math.random()*10+1);
		int index=(int)(Math.random()*10+1);
		a[N-1]=a[index];
		a[index]=temp;
		System.out.println(Arrays.toString(a));
		/*
		 * 数组上面已经构造完毕,下面进行算法处理
		 * 思路是这样的:
		 * 一个数和自己与是0,0和任何数异或是任何数,
		 * 所以我们就将整个数组所有的数异或,然后再和1到1000的
		 * 1000个数异或,这样子1-1000的数就会重复,异或的作用就是消除掉了
		 * 然后多出了一个重复的数,这个数就是要求的数
		 */
		temp=0;
		//从1到10的异或运算
		for (int i = 1; i < N; i++)
		{
			temp=temp^i;
		}
		for (int i = 0; i < a.length; i++)
		{
			temp=temp^a[i];
		}
		System.out.println(temp);
		/*
		 * 另外的暴力方法
		 * 思路如下:
		 * 就是新建一个数组用来储存某一个数在上面的的数组出现的次数
		 * 若出现,就在新建的数组的相应位置进行加一处理,然后在扫描
		 * 新的数组,若数组的值是2,这个位置的下标就是要找的值
		 */
		int[] b =new int[N];
		for (int i = 0; i < a.length; i++)
		{
			b[a[i]]++;
		}
		for (int i = 0; i < b.length; i++)
		{
			if(b[i]==2)
			{
				System.out.println(i);
				break;
			}
		}
	}

题2:一个数组,只有一个数是出现一次,其他都是出现了两次,叫你找出只出现一次的那个数(一直异或就行)

题3: 实现一个函数,输入一个整数,输出该数二进制表示中1的个数,比如4的二进制100,有一个是1,所以输出1

	@Test
	public void test2()
	{
		/*
		 * 实现一个函数,输入一个整数,输出该数二进制表示中1的个数
		 * 比如4的二进制100,有一个是1,所以输出1(这里写三种思路)
		 */
		/*
		 * 思路一:
		 * 就是int型的数字最多三十二位的,所以我们将1从第零位开始向左移x位,
		 * 然后和这个进行与运算,然后和1向左移x位的数字进行比较,若相等这个位置的数字就是1
		 */
		Scanner in=new Scanner(System.in);
		int a=in.nextInt();
		in.close();
		System.out.println(Integer.toBinaryString(a));
		int count=0;
		for (int i = 0; i < 32; i++)
		{
			if((a&(1<<i))==(1<<i))
			{
				count++;
			}
		}
		System.out.println("①:"+count);
		/*
		 * 思路二:
		 * 就是将这个数字进行无符号右移,然后再和1进行与运算
		 * 若是结果是1就证明该位是1,否则就是0
		 */
		count=0;
		for (int i = 0; i < 32; i++)
		{
			if(((a>>>i)&1)==1)
			{
				count++;
			}
		}
		System.out.println("②:"+count);
		/*
		 * 思路三:
		 * 先明白x&(x-1)的效果就是将x这个数字中的所有1中最低位的1消掉
		 * 这里说明一下,就是将一个数减一,要么是在最低位的1进行减一,
		 * 要么就是向高位进行借1,然后就会将所有的1中最低位的1的位置开始
		 * x-1和x的0和1是刚刚好相反的,这里比如:9  -->1001
		 * x  --->1001
		 * x-1--->1000
		 * 所以两个数做与运算的话就会将最后面那个1变成0了
		 */
		count=0;
		while(a!=0)
		{
			a=a&(a-1);
			count++;
		}
		System.out.println("③:"+count);
	}

题4:
用一条语句判断一个整数是不是2的整数次方

这个有前面的基础还是挺简单的,因为是不是2的整数次方
可以转化为是不是二进制中是不是只有一个1
这个就可以使用x&(x-)==0就可以判断了

题5:
将整数的奇偶位进行交换

	@Test
	public void test4()
	{
		/*
		 * 将整数的奇偶位进行交换
		 * 思路:
		 * 先将这个数和01 01 01 01 ....进行与运算得到的是奇数上的数字a,
		 * 偶数上的数字已经全部变成0了
		 * 然后将这个数和10 10 10 10 ....进行与运算,得到的是偶数上的数字b,
		 * 奇数上的数字已经全部变成0
		 * 然后将a左移一位得到c,b右移一位得到d
		 * 然后c和d进行异或运算得到交换的数
		 */
		int a=9;
		int ji=a&0x55555555;
		int ou=a&0xaaaaaaaa;
		a=(ji<<1)^(ou>>1);
		System.out.println(a);
	}

题6:
0-1间浮点数的二进制表示

	@Test
	public void test5()
	{
		/*
		 * 0-1间浮点数的二进制表示
		 * 给定一个介于0和1之间的实数,打印它的二进制表示
		 * 如果该数字无法精确使用32位以内的二进制表示,
		 * 则打印error
		 */
		double a=0.3;
		StringBuffer number=new StringBuffer("0.");
		a=a*2;
		while(a>0)
		{
			if(a>=1)
			{
				a=a-1;
				number.append("1");
				a=a*2;
			}
			else
			{
				number.append("0");
				a=a*2;
			}
			if(number.length()>34)
			{
				System.out.println("error");
				break;
			}
		}
		System.out.println(number.toString());
	}

题7:
数组中只有一个数出现了一次,其他数都出现了k次,请输出出现了一次的数

	@Test
	public void test6()
	{
		/*
		 * 数组中只有一个数出现了一次,其他数都出现了k次
		 * 请输出出现了一次的数
		 * 思路:
		 * 首先得知道这样一个知识点,比如:
		 * 两个相同的二进制数进行不进位加法时,结果就是0,
		 * 十个相同的十进制数进行不进位加法时,结果就是0,
		 * m个相同的m进制数进行不进位加法时,结果就是0,
		 * 可以自己写几个数验证一下,因为m进制,m个数,
		 * 这个数乘与m,最低位肯定是0,不进位,所以高位又是0
		 * 知道这些,然后就利用这个进行处理,就是将这些数变成k进制的
		 * 然后将他们进行不进位加法运算,出现了k次的数就会被消除掉
		 * 剩下的就是出现一次的数,然后将k进制转化成十进制
		 */
		int[] a= {2,2,2,9,9,9,7,7,7,3,3,3,6,6,6,0,0,0,123};
		char[][] b=new char[a.length][];
		int k=3;    //因为这里的数字都是出现3次
		int maxlength=0; //将最大的长度保存下来,因为后面求的数转化成十进制需要知道有多少位
		for (int i = 0; i < b.length; i++)
		{
			//将里面的数字全部转化成k进制,并且翻转过来
			b[i]=new StringBuffer(Integer.toString(a[i], k)).reverse().toString().toCharArray();
			if(b[i].length>maxlength)
			{
				maxlength=b[i].length;
			}
		}
		//然后进行不进位加法,就是将相应位的数字加起来,然后再相应地取k模
		int[] temp=new int[maxlength];
		for (int i = 0; i < b.length; i++)
		{
			for (int j = 0; j < maxlength; j++)
			{
				if(j>=b[i].length)
				{
					temp[j]=temp[j]+0;
				}
				else
				{
					temp[j]=temp[j]+(b[i][j]-'0');
				}
			}
		}
		//相应地取k模,然后转成十进制
		int number=0;
		for (int i = 0; i < maxlength; i++)
		{
			number=number+(temp[i]%k)*(int)(Math.pow(k, i));
		}
		System.out.println(number);
	}

你可能感兴趣的:(蓝桥杯和面试算法)