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