剑指Offer系列之题6~题10

6.用两个栈实现队列

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

考虑栈1用于存储元素,出队时,将栈1的元素压入栈2,此时栈2中元素从栈顶到底即其入队的顺序,然后出栈。若出队时栈2非空,则直接从栈2弹出元素。


1、根据栈2是否空将栈1元素全部压入:

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    //栈1存储元素,然后压入栈2,栈2的元素即进入队列的顺序
    public void push(int node) {
        stack1.push(node);
    }

    public int pop() {//每次出队时,判断栈2是否空,空则将栈1元素出栈后压入栈2,然后弹出栈2的栈顶元素
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

2、根据栈1是否只剩1个元素将栈1除栈底外元素压入:

该思路相比上一步增加了复杂度,不推荐。

//判断栈1是否为空,空则从栈2弹出栈顶元素(考虑两栈都空的异常)
//栈1非空则判断栈1是否只有一个元素。是则pop,不是则将除栈底外元素全部push到栈2

7.旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。

例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

考虑数组旋转后分为左边的递增数组和右边的递增数组,通过三个指针去判断,左、中、右。

当中间的元素大于等于左边元素时,说明其在左边的递增数组,此时最小值在右边。左指针移到中间位置。

当中间的元素小于等于右边元素时,说明其在右边的递增数组,此时最小值在左边。右指针移到中间位置。

当左=中=右时无法判断,顺序查找。退出条件为左右指针相邻,此时右指针的元素为最小值。


1、暴力解:

//遍历,输出最小值
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0){//异常
            return 0;
        }
        int min=array[0];
        for(int i=0;i<array.length-1;i++){
            if(array[i]>array[i+1]){
                min=array[i+1];
                break;
            }
        }
        return min;
    }
}

2、二分查找:

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0){//异常
            return 0;
        }
        int len=array.length;
        int left=0;//左指针
        int right=len-1;//右指针
        int mid=(left+right)/2;
        if(array[left]<array[right]){//若小于则证明该数组是升序
            return array[left];
        }
        //特殊情况,左、右、中三值相等,此时下面的方法无法判断。需要单独处理这种情况
        if(array[left]==array[mid] && array[right]==array[mid]){
            int min=array[0];
            for(int i=0;i<len-1;i++){
                if(array[i]>array[i+1]){
                    min=array[i+1];
                    break;
                }
            }
            return min;
        }

        while(right!=left+1){
            if(array[mid]<=array[right]){
                //说明在右边的递增数组,最小值在该元素前面
                right=mid;
                mid=(left+right)/2;
            }
            if(array[mid]>=array[left]){
                //说明在左边的递增数组,最小值在该元素后面
                left=mid;
                mid=(left+right)/2;
            }
        }
        return array[right];
    }
}

8.斐波那契数列

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项为1)。n<=39

了解斐波那契数列的特性 f(n)=f(n-1)+f(n-2){n>=2};


1、递归:

public class Solution {
    public int Fibonacci(int n) {
        //n<=39  此处f(n)=f(n-1)+f(n-2){n>=2};
        if(n==0){
            return 0;
        }else if(n==1){
            return 1;
        }else{
            return Fibonacci(n-1)+Fibonacci(n-2);
        }
    }
}

该暴力解法存在效率方面的缺点,会重复计算大部分值。

2、利用循环:

public class Solution {
    public int Fibonacci(int n) {
        //n<=39  此处f(n)=f(n-1)+f(n-2){n>=2};
        if(n==0){
            return 0;
        }else if(n==1){
            return 1;
        }else{
            int f1=0;
            int f2=1;
            int fn=0;
            //利用循环
            for(int i=2;i<=n;i++){
                fn=f1+f2;
                f1=f2;//向后移一位
                f2=fn;
            }
            return fn;
        }
    }
}

9. 跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

斐波那契数列的应用


1.递归:

public class Solution {
    public int JumpFloor(int target) {
        //斐波那契数列的应用
        if(target==1){
            return 1;
        }else if(target==2){
            return 2;
        }else{
            return JumpFloor(target-1)+JumpFloor(target-2);
        }
    }
}

2.循环:

public class Solution {
    public int JumpFloor(int target) {
        //斐波那契数列的应用
        if(target==1){
            return 1;
        }else if(target==2){
            return 2;
        }else{
            int f1=1;
            int f2=2;
            int fn=0;
            for(int i=3;i<=target;i++){
                fn=f1+f2;
                f1=f2;
                f2=fn;
            }
            return fn;
        }
    }
}

10.变态跳台阶 🔺

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

关键是求出f(n)的公式。

假设n级台阶,每一次可以跳1、2、3……n阶;f(1)=1;f(2)=2

那么跳n级台阶的跳法f(n)=f(n-1)+f(n-2)+f(n-3)+……+f(n-n)

上述公式的解释:总跳法=第一次跳一级之后的跳法+第一次跳2级之后的跳法+……第一次跳n级的跳法=f(n-1)+f(n-2)+……+f(2)+f(1)+f(0);其中f(n-1)=f(n-2)+f(n-3)+……+f(1)+f(0);

所以f(n)=2*f(n-1)=2*2*f(n-2)=2^((n-1)*f(1)) =2^(n-1)


另一解释:每个台阶可以看作一块木板,让青蛙跳上去,n个台阶就有n块木板,最后一块木板是青蛙到达的位子, 必须存在,其他 (n-1) 块木板可以任意选择是否存在,则每个木板有存在和不存在两种选择,(n-1) 块木板 就有 [2^(n-1)] 种跳法,可以直接得到结果。


1.循环:

//同上题,该题仍有递归和循环两种解法
public class Solution {
    public int JumpFloorII(int target) {
        //1:1; 2:2 3:4; 4:8  2^(n-1)
        if(target==1 || target ==2){
            return target;
        }
        int res=2;
        for(int i=2;i<=target-1;i++){
            res=res*2;
        }
        return res;
    }
}

如有错误,欢迎指正

posted @ 2020-04-11 12:13  雨落成尘  阅读(156)  评论(0编辑  收藏  举报