剑指offer系列(9)——二进制中一的个数

题目如下:

      请实现一个函数,输入一个整数输出该数二进制表示一的个数,例如把9表示成二进制,是1001,有两位是1,因此如果输入就该函数输出2。

      刚开始看到这个题,我们就很容易想出这样一个方法。我们先判断这个数的从右边数第一位是不是一?这个可以很简单的用这个数与1进行与操作来实现。之后我们将这个数向右移一位。再像刚才一样判断从右边数第二位是不是一,我们可以一直这样循环下去,一直到这个数变为零为止。

      这种方法很好想到也很好实现,但是有一个问题,当我们这个数是负数的时候,但他右移会补一。最后会使我们这个数变为0x1111111。我们这个循环就会变成了死循环。这个循环永远也不会结束。

       

       这个问题有两个解决方法,第一,我们可以使用java中的无符号右移。第二,我们可以不用这个方法,改为将一进行左移。我们知道将一左移是不会出现补一这种情况的。因此可以规避这个问题。这两种方法的实现分别如下:
   第一种方法:
private static int findNumberOf1One(long test) {
	// TODO Auto-generated method stub
	/*
	 * test不断右移和1相与的方法
	 * 
	 */
	
	if (test > Long.MAX_VALUE || test < Long.MIN_VALUE) {
		throw new  RuntimeException("输入数字过大或过小");
	}
	
	int num = 0 ;		 //1的个数
	int x = 1 ;			 //相与的对象
	long tmp = test;		 //中间变量	
	
	
	while (tmp != 0) {
		
		if ((tmp&x) != 0) {
			num++;
		}
		
		tmp = tmp >>> 1;
		
	}
	return num;
}

    第二种方法:

	private static int findNumberOf1Second(long test) {
		// TODO Auto-generated method stub
		/*
		 * test 与 1及1左移后的数相与的方法
		 * 
		 */
		
		if (test > Long.MAX_VALUE || test < Long.MIN_VALUE) {
			throw new  RuntimeException("输入数字过大或过小");
		}
		
		int num = 0 ;		 //1的个数
		long x = 1 ;			 //相与的对象
		long tmp = test;		 //中间变量	
		
		
		while (0 != x) {
			
			if ((tmp&x) != 0) {
				num++;
			}
			
			x = x << 1;
			
		}
		return num;
	}

      

       最后,剑指offer上还写了这样一种解法。我们可以利用这样一个规律,把一个整数减去一再和原整数作与操作,会把该整数最右边一个一变为零,那么一个整数的二进制表示中有多少个一,就可以进行多少次这样的操作。

       这里,我们举个例子来看:

       若一个数末尾是1 :       例如 -1:    补码表示为  1111...1111(16个1)   减去1 ,变成-2  即1111...1110

       若一个数末尾是1:        例如9 :      补码表示为     0000..........1001          减去1, 变成8   即1111...1000

      实现代码如下:

private static int findNumberOf1Third(long test) {
		// TODO Auto-generated method stub
		/*
		 * 将test 与 test - 1相与的方法
		 */
		if (test > Long.MAX_VALUE || test < Long.MIN_VALUE) {
			throw new  RuntimeException("输入数字过大或过小");
		}
		int num = 0;
		while(test != 0){
			num++;
			test = test&test-1;
		}
		
		return num;
	}



你可能感兴趣的:(数据结构与算法)