String类型的超大的数字转2进制输出(算法面试)

  现在是临放假回家的节奏,前两天晚上在领导的逼迫下,改bug改到凌晨一点,终于把这次的项目算是推动上线了,话说,明天就要回家了,今天无聊,刚好就抽点时间敲一下前两天高德面试的一道算法题目。


前两天去高德地图面试,面试的最后一道题是一道算法题,当时不给我纸笔,让我硬用手写,用手写我是写不出来的(中途不能调试不能换行呀!),刚刚吃饱饭,正好也有空,就把这道题敲下来了。


先说一下题目,题目是这样的,我们都知道十进制转二进制是用的求二取余法(不知道的自己去百度哈),现在给定一个“超大的String类型的数字”,让转换成2进制。


让我们来看一下,为什么这个数字是String类型的呢,在java中Int类型是32位的,这也就说明了很多时候会数据溢出,当一个数字足够大,比如“289157293842893489023478234”这个数字的时候,Int型就无法表示了,即使是Float或者double型,也是有位数限制的。而我们常用的加减乘除方法都是在数字上进行操作的,换句话说,当这个数字足够大, 大到可能有100位数,一亿亿亿亿亿。。的时候,我们就已经没法用通用的数字计算法计算了。那肿么破?


首先,这个数字,已经破灭了我们强转Int然后做除取余的思路了,那我们就把整个思路细化,小学时候我们怎么算的除法?根号方式算的。也就是322除以3 这个题目怎么算?用小学的方法来看,先取出第一位,也就是3。3除以3结果是10。下一步取出321的第二位数字“2”,2除以3,结果02。继续往后算,把十位的余数取出来,跟后面的1组合,成22,22除以三,结果的71。这样结果就出来了,结果是107,余数是1,这样的话,就把整个除法分解开来了,每次是一到两位数字,这样我们就可以做除法了。以上我们做的,其实就是把一次的除法模拟出来了。具体如下,字写得比较烂。


我们已经模拟出来算法的思路了,剩下的就是 代码了,那这个类的代码如下:

package com.example.test;

public class StringConvert {
	
	private static StringBuilder strRestult;//这个变量是用于存储结果,StringBuilder在频繁变化时候,要比String更高效,而且整个过<span style="white-space:pre">						</span>程不涉及多线程,所以不用考虑线程安全,所以我们整体上都使用的StringBuilder
	private static StringBuilder sb;<span style="white-space:pre">	</span>//这个变量用于存储每次除法之后算出来的结果,用于下次计算除法
	//计算方法
	public StringBuilder Convert(String input){
		strRestult = new StringBuilder();//初始化变量
		sb = new StringBuilder(input);<span style="white-space:pre">	</span>//初始化变量
		//只要模拟除法得出的数字为2或者更大的数,那就说明可以继续模拟除法,要么是两位数或者两位数以上,要么是一位数,但是这个<span style="white-space:pre">		</span>数字必须大于一
		while(sb.length()>1||(sb.length()==1&&Integer.parseInt(sb.substring(0,1))>1)){
			sb = getTempString(sb);
		}<span style="white-space:pre">																</span>//求二取余这个算法最终的一位是算到最后的结果,如果是0的话,就不需要加到首位了,只有1才需要加到首位
		if(Integer.parseInt(sb.substring(0,1))==1){
			strRestult.insert(0, sb.toString());
		}
		
		return strRestult;
	}
	
	
	/**
	 * 该方法是模拟除法的某一一次除法过程,把这个过程抽取出来成为一个方法,这样使代码更容易理解,避免堆一起成一坨***
	 * 比如324/2  则先算3/2=2余1  第二步是1*10+2/2=6 余0,第三步骤是4/2=2  StringBuilder为162,用StringBuilder是因为在字符串拼接过程中不需要新开地址空间。
	 * Stribuder不用重开地址空间,而String是特殊类型,会重新开辟地址空间,影响程序效率。
	 * @param str
	 * @return 某次模拟除法的结果,下次将用这个结果去继续模拟除法
	 */
	private StringBuilder getTempString(StringBuilder str){
		int p = 0; //
		sb = new StringBuilder();//每次计算,都需要sb初始化,因为需要计算的参数已经当做形参传进来了,现在的sb只需要做记录结<span style="white-space:pre">					</span>果就可以了
		for(int i=0;i<str.length();i++){
			p = p+Integer.parseInt(str.substring(i, i+1));
			if(p%2==0){//除以2整除
				sb.append(p/2);
				p=0;
			}else{//没有整除,有余数,比如324/2,高位数余一,则模拟除法的下一步是12/2
				sb.append(p/2);
				p=10;
			}
		}
		//考虑到第一位可能为零,比如15,第一位取出来为1/2=0;所以最后要干掉这位数字(如果为0的话)
		if(Integer.parseInt(sb.substring(0,1))==0){
			sb.deleteCharAt(0);
		}
		if(p==0){
			strRestult.insert(0, 0+"");
		}else{
			strRestult.insert(0, 1+"");
		}
		return sb;
	}
}

上面就是整体的算法,整体上不是很麻烦,但是要考虑的细节很多,是否需要对外开放public权限,当然,如果这是个工具类的话,完全可以让计算方法改成static方法。整体上计算结果基本是要考虑最后一位的情况下怎么处理,因为2进制的首位数字肯定是1,所以当为0的情况下就不在前面加上了。具体的模拟除法的步骤上要考虑数字的第一位数字,是否大于2,如果不是大于二的数,结果也容易出现0的情况,但是考虑到循环问题,如果每次都判断的话,那就消耗计算,所以采取通用算法,然后处理结果,如果stringbuilder的首位为0,则删除就好,这样就可以不影响计算了。


先这样~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值