大神 地址 :http://blog.csdn.net/v_JULY_v/article/details/6015165
27.跳台阶问题
题目:一个台阶总共有 n 级,如果一次可以跳 1 级,也可以跳 2 级。
求总共有多少总跳法,并分析算法的时间复杂度。
这一题 要结果 应该不难的。 就是 按顺序来。 先算 有0个 跳2级的情况。 再算 有1个跳2级的情况(位置 遍历前移。) 然后2个跳2级。。。。。。 递归计算。。 不过 毫无疑问 这个算法的复杂度 很复杂。。
先写个复杂的这个来看看:
。。我一开始想复杂了 其实 就是 f(n)=f(n-1)+f(n-2) 因为 在某一步。 可以选择跳一级 也可以选择跳2级。 跳一级 就是f(n-1) 2级就是 f(n-2) 。 这个就简单了。用递归能实现 而且 之前有一题 需求是 要求效率的 递归效率太低。 正推就ok了 就是 先算f(3) 得出后 再算f(4)...一直到 f(n) 一次循环就可以得出。
28.整数的二进制表示中 1 的个数
题目:输入一个整数,求该整数的二进制表达中有多少个 1。
例如输入 10,由于其二进制表示为 1010,有两个 1,因此输出 2。
这一题 如果 要求效率什么的 铁定是 移位 然后 与1 取与操作。 不停的移位 就ok了 算法没什么吧。。。
看了答案 思路是对的 不过 忽视了 负数。因为负数 会 补1. 换个思路 就是把1 左移 来与。 ok了。。
29.栈的 push、pop 序列
题目:输入两个整数序列。其中一个序列表示栈的 push 顺序,
判断另一个序列有没有可能是对应的 pop 顺序。
这个 题目 最简单的方法就是走一遍。 遍历第一个序列。 push 一个 判断 并且和第二个序列的比较。 如果相等 就pop 并且第二个序列的当前对象 移除。 继续判断当前。如果还相等 继续做这个操作。 一直到不相等了 就继续 push 。 如果 最后全部push完成 并且 还不相等。第二个序列还有值 就说明 是错的。
代码如下
public boolean test(List<Integer> pushArr,List<Integer> popArr){
int pushIndex=-1;
List<Integer> myPushArr=new ArrayList<Integer>();//我用一个 list 和一个变量 i 模拟栈
int popIndex=0;//记录 popArr 的index
for(int j=0;j<pushArr.size();j++){
int data=pushArr.get(j);
int popData=popArr.get(popIndex);//当前出栈。
if(data==popData){//相等 就是 进栈后立刻出栈。
popIndex++;
boolean sign=true;
while(sign&&pushIndex>=0){
data=myPushArr.get(pushIndex);
popData=popArr.get(popIndex);
if(data==popData&&pushIndex>=0){
pushIndex--;
popIndex++;
}else{
sign=false;
}
}
}else{
//需要进栈。
pushIndex++;
if(pushIndex>=myPushArr.size()){
myPushArr.add(data);
}else{
myPushArr.set(pushIndex, data);
}
}
}
return pushIndex<0;
}
30.在从 1 到 n 的正数中 1 出现的次数
题目:输入一个整数 n,求从 1 到 n 这 n 个整数的十进制表示中 1 出现的次数。
例如输入 12,从 1 到 12 这些整数中包含 1 的数字有 1,10,11 和 12,1 一共出现了 5 次。
分析:这是一道广为流传的 google 面试题。
这一题 应该 有点移位的思想来做。 但是 不是 移位操作,是对于十进制来操作。 仔细想想其实有点数学的那个 C 那个 组合更简单。就是在 m 中 取n 个有多少种取法。
1.获取这个数 有多少位。
2 假设5位 先计算 4位(9999 )里面有多少个1 就是 4中有一个1的个数*999(这里不应该是999 应该是 9*9*9 这是因为 每种组合的情况 其他位数都可以是 0到9 除了1之外的任何数字) 4位数中有2个1的个数 *2 *999 4位数中有3个1的个数*3*999 4位数很重有4个1的个数 * 4*999
3 再计算 从 10000 到给定数字的 有 1的个数。
当然 也可以 循环 从 1 一直到 给定的数字 一个个判断 不过 我估计 第一个效率要好很多。 2个代码都写一下 比较一下。
//第一种 大众的方法
public long getOneTime2(long data){
int time=0;
for(int i=1;i<=data;i++){
//System.out.println("看看"+i);
time+=getOneTimeByNum(i);
//System.out.println("看看time"+time);
}
return time;
}
public long getOneTimeByNum(long data){
long re=0;
while(data>0){
long d=data%10;
data/=10;
if(d==1){
re++;
}
}
return re;
}
//第二种 方法 复杂一点但是快很多
public long getOneTime(long data){
if(data==0){
return 0;
}
if(data<10){
return 1;
}
int size=getSize(data);
long time=0;
// long 这个是考虑 第一位是1 的情况 有多少个。 因为第一位是1 比较特殊
long firstOneTime=1;
for(int i=1;i<=size-1;i++){
time+=(getCombination(size-1, i)*i);
firstOneTime*=10;
}
//到这里 只算了 第一位是0的时候的个数
//如果是1 再加 10000(size-1 个 0) 如果是2 还是这么多。。但是 最后一个 就不容了
//比如 给定的 是 3896 第一位 是0 假设有 100个; 第一位 是1 有 100+1000个 第一位是2 有100个 但是 第一位是3 就不同了 需要特殊处理。
//先获取到第一位的数字
long firstNum=data;
while(firstNum>=10){
firstNum/=10;
}
//所以 现在1的个数 应该等于
if(firstNum==1){
//很特殊
time+=data-firstOneTime*firstNum+1;//出去第一位 有多少个数 就有多少个情况 还要kaolv 1000000000 这种 需要+1
}else{
//就是大约1的情况了
time*=firstNum;//取决于 第一位的 种类。 需要注意的是 最后一个 需要特殊处理的。第一个数字是1 的也要特殊处理
time+=firstOneTime;//当时1的情况 仍然很特殊
}
//递归计算 去掉第一位之后 余下的情况
long nowData=data-firstOneTime*firstNum;
long newTime=getOneTime(nowData);
// System.out.println("data:"+data+"firstOneTime"+firstOneTime+"nowData:"+nowData+"newTime:"+newTime);
return time+newTime;
}
public int getSize(long data){
int s=0;
while(data>0){
s++;
data=data/10;
}
return s;
}
/*
* 获取组合个数 结果 已经 要乘以 m-1 位 9 相乘 这是因为 每种组合的情况 其他位数都可以是 0到9 除了1之外的任何数字。
*/
public long getCombination(long m,long n){
if(m==n){
return 1;
}
long otherTime=1;
for(int i=0;i<m-n;i++ ){
otherTime*=9;
}
long mm=m;
long time=Math.min(m-n,n);
long nn=time;
long nnn=nn;
for(int i=0;i<time-1;i++){
m--;
nn--;
mm*=m;
nnn*=nn;
}
return mm/nnn*otherTime;
}
第一种 是大众 遍历方法。 写起来很简单 运算起来 。。。
第二种 快很多。非常多。 我的机器 第一种超过8位算不动了 电脑 第二种 一直到 越界了(long 类型) 都能 瞬间运算出来。