算法分析:x+y=x|y,求k小y

题目如下:

已经公式 :x+y=x|y,0< x, y < 2,000,000,000,求使公式成立的第k小的y值。

       输入:x k

      输出:y

     示例:

               输入:5 2

               输出:8


               输入:5678 88888

               输出:11372928


               输入:345678989 1234565

               输出:1246040098


    注意:允许时间1s。(爆力解很容易超时)

     解析:2000,000,000足够用int类型存下了,故定义一个位数组,32位(4个字节,初始值全部为0),把x(换算成二进制形式)以右端对齐填入数组中,再把k(换算成二进制形式)也以右端对齐填入数组中取值为0的位置(x+y与x|y的区别在于是否进位,如果保持x+y不进位就可以了。为什么要把k的二进制填充到取值为0的位?因为若x的二进制某一位为1,则y对应位应当为0,否则就会进位。同时直接填充k是由依次搜索推出来的结论,设想一下,依次从右往左搜索与直接填充k的二进制原理是相同的),java代码如下:


package test;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;



public class Test {

	static Test main = new Test();
	class Node{
		public int pos; //用于存数的位置
		public int value; //该位置的值
		public boolean flag = false; //该位置是否已经设置过
		public Node(){
			
		}
		public Node(int pos, int value){
			this.pos = pos;
			this.value = value;
		}
		public int getPos() {
			return pos;
		}
		public void setPos(int pos) {
			this.pos = pos;
		}
		public int getValue() {
			return value;
		}
		public void setValue(int value) {
			this.value = value;
		}
		public boolean isFlag() {
			return flag;
		}
		public void setFlag(boolean flag) {
			this.flag = flag;
		}
		
		
	}
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		while(scan.hasNext()){
			int x  =scan.nextInt();
			int k = scan.nextInt();
			
			// 爆力方法
			long pre1 = System.currentTimeMillis();
			normCal(x, k);
			long aft1 = System.currentTimeMillis();
			System.out.println("method 1 takes: "+(aft1-pre1)+" ms!");
			
			// 简便方法 1  (向x的非1位置从右依次填充k的二进制)
			long pre2 = System.currentTimeMillis();
			unNormalCal(x, k);
			long aft2 = System.currentTimeMillis();
			System.out.println("method 2 takes: "+(aft2-pre2)+" ms!");
			
			// 简便方法 2 
			long pre3 = System.currentTimeMillis();
			unNormalCal3(x, k);
			long aft3 = System.currentTimeMillis();
			System.out.println("method 3 takes: "+(aft3-pre3)+" ms!");
			
		}
	}
	
	
	// unNormalCal method(简便方法2,是一位大神想出来的,速度更不用说了)
	private static void unNormalCal3(int x, int k) {
		int y = 0, n =1;
		while(k>0){
			if(x%2!=0){
				//此时x的二进制最右端为1的话,一直使x右移,就是找到x的为0的位置
				while(x%2!=0){
					n = n*2;  //每移一位,n记录一下变化的值
					x = x/2;
				}
			}
			//如果k的二进制最右端为1,就使y加上n
			if(k%2!=0)
				y = y+n;
			
			n  = n*2;
			x = x/2;
			k = k/2; //同时使x,k右移,以便下一步判断
		}
		System.out.println(y);
	}

	// unNormalCal method(简便方法)
	private static void unNormalCal(int x, int k) {
		//2,000,000,000足够用int类型来存,4个字节,共32位,开始置每位为0
		List list = new ArrayList();
		for(int m =  0;m<32;m++){
			Test.Node node = main.new Node(m, 0);
			list.add(node);
		}
		
		//把x转成二进制,并把x的二进制填充到32位中
		char b_x[] = Integer.toBinaryString(x).toCharArray();
		for(int i = 0; i=0;i--){
			for(int j=list.size()-1;j>=0;j--){
				Test.Node node = list.get(j);
				if(!node.isFlag()){
					//如果没有访问过,就设置该位
					node.setValue(Integer.valueOf(String.valueOf(b_k[i])));
					node.setFlag(true);
					break;
				}
			}
		}
		
		//res_b用来存放4个字节中的所有二进制位
		String res_b = "";
		for(int j= 0;j=0;i--){
			x = (int) (x + Integer.valueOf(String.valueOf(s_arr[i]))*Math.pow(2, s_arr.length-1-i));
		}
		return x;
	}

}

执行结果如下:

5678 88888
11372928
method 1 takes: 16 ms!
11372928
method 2 takes: 15 ms!
11372928
method 3 takes: 0 ms!


345678989 1234565
1246040098
method 1 takes: 3541 ms!
1246040098
method 2 takes: 16 ms!
1246040098
method 3 takes: 0 ms!




执行时间相差甚大!!!

              

你可能感兴趣的:(算法)