剑指Offer算法题JAVA版4-12题(全是个人写的非官方,只供参考和自己复习,测试用例都通过了)

4.输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

(思路:用递归不断的将中序序列分成左右,两部分。)

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
  public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
	    	Solution s= new Solution();
	    	
	    	TreeNode node=s.creatTree(pre,0,pre.length-1, in,0,in.length-1);
	    	return node;
	    
	    }
	    public  TreeNode creatTree(int[] per,int perStart,int perend,int[] in, int inStar,int inEnd){
	    
	    if(perStart>perend||inStar>inEnd){//叶子节点左右孩子皆为Null
	    	return null;
	    }else{
	    TreeNode node=new TreeNode(per[perStart]);
	    	int middle=-1;
	    	for(int i=inStar;i<=inEnd;i++){//当前节点,在中序遍历序列中的位置
	    		if(in[i]==per[perStart]){
	    			middle=i;
	    		}
	    	}
	    	node.left=creatTree(per,perStart+1,middle-inStar+perStart,in,inStar,middle-1);
	    	node.right=creatTree(per,middle-inStar+perStart+1,perend,in,middle+1,inEnd);
	    	return node;
	    }
	    	
	    }
}


5.用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

(思路:第一个栈专门放入,第二个栈负责弹出,出栈的时候,如果第二个栈是空的,将第一个栈的所有内容弹出并放入第二个栈,之后从第二个栈弹出,如果第二个栈不为空,则直接弹出。)

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    public void push(int node) {
	        stack1.push(node);
	    }
	    
	    public int pop() {
	    if(stack2.isEmpty()){
	    	while(!stack1.isEmpty()){
	    		stack2.push(stack1.pop());
	    	}
	    	
	

 
6 .把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
/*考虑了出现,相同数字的情况的二分查找法,经测试,只要复合旋转规范,无论是否有重复数字都可通过,时间复杂度O(logn)*/
public int minNumberInRotateArray(int [] array) {
   if(array==null){
   return 0;
   }else{
   int pre=0;
   int end=array.length-1;
   while(pre!=end){
   if(array[pre]==array[end]){//这里处理出现相同数字的情况
   pre++;
   end--;
   }else if(array[pre]<array[end]){
   end=pre;
   }else if(array[pre]>array[end]){
   ;
   double d=((double)(pre+end))/2;
   int middle=(int)Math.ceil(d);
   
   if(array[middle]>array[end]){
   pre=middle;
   }else if(array[middle]<array[end]){
   end=middle;
   }else if(array[middle]==array[end]&&middle==end){
   pre=middle;
   
   }else if(array[middle]==array[end]&&middle!=end){//相同数字处理
   end=middle;
   }
   }
   }
   return array[pre];
   }
   
   }


7.大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。

(用递归完全行不通,运算时间爆炸,用动态规划完成,

测试用例里肯定准备着一个超大的n来让Stack Overflow,为什么会溢出?因为重复计算,而且重复的情况还很严重,举个小点的例子,n=4,看看程序怎么跑的:
Fibonacci(4) = Fibonacci(3) + Fibonacci(2);
                    = Fibonacci(2) + Fibonacci(1) + Fibonacci(1) + Fibonacci(0);
                    = Fibonacci(1) + Fibonacci(0) + Fibonacci(1) + Fibonacci(1) + Fibonacci(0);
由于我们的代码并没有记录Fibonacci(1)和Fibonacci(0)的结果,对于程序来说它每次递归都是未知的,因此光是n=4时f(1)就重复计算了3次之多。
那么如何求解呢,动态规划似乎不错,关于动态规划三个条件:最优子结构、无后效性、子问题重叠这些就不谈了
  public int Fibonacci(int n) {
				   if(n==0){
					   return 0;
				   }else if(n==1){
					   return 1;
				   }else{
					  int b=0;//等价与F(n-2)
					  int a=1;//等价与F(n-1)
					  int f=0;
					  for(int i=2;i<=n;i++){
						  f=a+b;//F(n)=F(n-1)+F(n-2)
						  b=a;//下次循环F(n+1)=F(n)+F(n-1),故b=a,等价与令b等于当次循环的F(n-1),也就是下次循环的F(n-2)。
						  a=f;//同上,当次的F(n)是下次的F(n-1)a为下次的F(n-1)
			
					  }
					  return f;
							   
							 
				   }
			    }


8.一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

(思路这道题,关键在于问题的分析,如果你能很清楚的分析出这个一斐波那契数列问题,F(n)=F(n-1)+F(n-2),那做法,就如上题一样了,分析下思路,比如,青蛙想跳到第3层,那么它有两种选择,1。是从第一层跳两阶上来【连续跳两次不行,因为它跳到第二层的时候,就与后者冲突了】,2.是从第二层跳1阶上来,总共F(3)=F(2)+F(1))

public int JumpFloor(int target) {
			if(target==1){
				return 1;
			}else if(target==2){
				return 2;
			}else{
				int a=1;
				int b=2;
				int f=0;
			for(int i=3;i<=target;i++){
				f=a+b;
				a=b;
				b=f;
			}
			return f;
			}
	    }


9..变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

(难点在于,推算出f(n)=2f(n-1),如果用f(n)=f(n-1)+f(n-2)。。。。。f(0)来递归,运算时间过长过长。可以尝试用动态规划来做,用数组来存中中间运算出来的数值,明天尝试。)

 public int JumpFloorII(int target) {
			
			if(target==1){
				return 1;
			}else{
				return 2*JumpFloorII(target-1);
		 
			}
			
	    }

9.2用循环实现,不知这样能不能称之为动态规划,还需对动态规划的概念在进行了解。

 /*思路,用一个数组来记录所有的f(n),f(n-1).....f(0)*/
public int JumpFloorII(int target) {
			
			if(target==0){
				return 1;
			}else{
				int[] k=new int[target+1];
				k[0]=1;
				for(int i=1;i<=target;i++){
							for(int j=0;j<i;j++){
								k[i]=k[i]+k[j];
							}
				}
	return k[target];
			}
			
			
	    }

10. 矩形覆盖

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?(思路同青蛙跳台阶,也为斐波那契数列)

public int RectCover(int target) {
			if(target<=2){
				return target;
			}else{
				int a=2;//f(n-1)
				int b=1;//f(n-2)
				int f=0;//f(n)
				for(int i=3;i<=target;i++){
					f=a+b;//f(n)=f(n-1)+f(n-2)
					b=a;//更新b为f(n-1);用于下轮循环,下轮为f(n+1)=f(n)+f(n-1)
					a=f;//更新a为f(n);用于下轮循环,下轮为f(n+1)=f(n)+f(n-1)
				}
				return f;
			}
			
	    }

11. 二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

(思路,让该数,与1做与运算,得1,count++,之后该数,向右移一位,高位要补零,防止负数高位补1要用>>>来移位,高位只补零)

public int  NumberOf1(int n) {
	         int flag=1;
	         int count=0;
	         while(n!=0){
	        	 if((n&flag)==1){
	        		 count++;
	        	 }
	        	 n>>>=1;
	         }
	         return count;
	           
	     }

12. .数值的整数次方

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

(思路:举例2^13相当于10^1101=10^0001*10^0100*10^1000=2^1*2^4*2^8=2^1*(2^2)^0*(2^2^2)^1*(2^2^2^2)^1(移一次位,基数要平方一次),我们就可以通过,操作二进制的方式,来做,这样做,多大的int型的次方,都可以在32次循环内,得出结果。)

  public double Power(double base, int exponent) {
			  int n=exponent;
			  double base2=base;
			  double result=1;
			  if(exponent<0){
				  if(base==0){
						 throw new RuntimeException("零不能做分母");
					  }
				  n=-exponent;
			  }else if(exponent==0){
				  return 1;
			  }
			  while(n!=0){
				  if((n&1)==1)
					  result*=base2;//
				  base2*=base2;//比如2^13=2^1*2^4*2^8=2^1*4^0*16^1*256^1;每次移动一位,都要把基数来一次平方。
				  n>>>=1;
				  
			  }
			  return exponent>0?result:(1/result);
			 
		        
		  }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值