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

来源:互联网 发布:晨兴资本 知乎 编辑:程序博客网 时间:2024/06/05 15:22

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);           }



0 0
原创粉丝点击