剑指offer答题总结篇(1)Java实现

本文精选《剑指Offer》中的经典题目,包括二维数组查找、字符串空格替换、链表逆序打印、二叉树重建、双栈实现队列、旋转数组最小值、斐波那契数列、台阶跳法计数等,提供详细的解题思路和代码实现。

题目描述

       1、在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

/**
 * 根据题意,可以考虑从左下角向右上角开始遍历
*/
public class Solution {
    public boolean Find(int target, int [][] array) {
        int len = array.length-1;    //行数,最后一行
        int i = 0;
        for(int j=0; len>=0 && i<array[0].length; j++){    //从左下角开始遍历
            if(target>array[len][i]){    //如果比左下角元素大,则右移
                i ++;
            }else if(target<array[len][i]){    //小则上移
                len --;
            }else{
                return true;
            }
        }
        return false;
    }
}

      2、请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

public class Solution {
    public String replaceSpace(StringBuffer str) {
    	if(str == null){
            return null;
        }
        for(int i = 0; i<str.length();i++){
            if(str.charAt(i) == ' '){    //遍历字符串的每一个字符,为空格就替换
                str.replace(i,i+1,"%20");    //因为是StringBuffer,直接替换位置
            }
        }
        return str.toString();    
    }
}

         3、输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> al = new ArrayList<>();
        while(listNode != null){    //直到遍历到链表的尾部
            al.add(0,listNode.val);    //根据集合的性质,每次都把每次都把新元素放在首部
            listNode = listNode.next;
        }
        return al;
    }
}

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

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre == null || in == null){
            return null;        
        }
        TreeNode root = reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
        return root;
    }
    private TreeNode reConstructBinaryTree(
        int[] pre,int startPre,int endPre,int[] in,int startIn,int endIn){
        if(startPre>endPre||startIn>endIn){
            return null;
        }
        //前序遍历的第一个数字是根节点的值
        TreeNode root = new TreeNode(pre[startPre]);
        //遍历中序数组,找到根节点的位置,创建左右子树
        for(int i = startIn;i <=endIn; i++){
            if(in[i]==pre[startPre]){    //通过找到根节点将其分为左右子树
                //将左子树作为整棵树重新遍历,并将返回值作为上一个节点的左节点
                root.left = reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);    
                //将右子树作为整棵树遍历
                root.right = reConstructBinaryTree(pre,startPre+i+1-startIn,endPre,in,i+1,endIn);        
            }
        }
        return root;
    }
}

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

/**
	 * 思路分析:
	 * 1、队列特性,先进先出,栈特性,先进后出
	 * 2、两个栈,stack1先存储进来的值,然后将stack1的内容压入stack2,从而实现按顺出弹出
	 * 3、弹出栈顶元素后,在将stack2的元素重新压入stack1
	 * 4、循环2,3步,实现队列
	 */
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() {
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());    //将stack1的数据压入stack2,实现反转
        }
        int result = stack2.pop();    //获取进栈的第一个元素
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());    //重新压入stack1
        }
        return result;
    }
}

6、把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

/**
	 * 思路分析:如果用普通的遍历方法,效率太低,所以建议使用二分法查找
	 * 1、每次获取数组中中间元素的值
	 * 2、分别与左右边界元素比较,缩小查找范围
	 * 3、知道查找到最后两个元素,因为为非减排序,则右元素即为所求元素
	 */
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if (array.length == 0){
            return 0;
        }
        int left = 0;
        int right = array.length - 1;
        int middle = -1;
        while (array[left]>=array[right]) {
            if(right-left==1){    //如果只有两个元素时,说明右元素为所求元素
                middle = right;
                break;
            }
            middle = left + (right - left) / 2;    //获取中间元素指针位置
            if (array[middle] >= array[left]) {    //如果中间元素大于左边界元素,说明最小值在右部分
                left = middle;
            }
            if (array[middle] <= array[right]) {
                right = middle;
            }
        }
        return array[middle];
    }
}

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

n<=39

/**
	 * 斐波那契数列(Fibonacci sequence):指的是这样一个数列:
	 * 		1、1、2、3、5、8、13、21、34、……在数学上,
	 * 		斐波纳契数列以如下被以递推的方法定义:
	 * 			F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)
	 */
public class Solution {
    public int Fibonacci(int n) {

        int f1=1;    
        int f2=1;
        int sum = 0;
        if(n == 0){
            return 0;
        }else if(n ==1 || n == 2){    //两个以内直接到达
            return 1;
        }else{
            for(int i=3;i<=n;i++){    //从第三个数开始
                sum = f1 + f2;        
                f1 = f2;
                f2 = sum;
            }
        }
        return sum;
    }
}

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

/**
	 * 思路分析:
	 * 	举例:到达台阶5可以分为:3->5,4->5 两种
	 * 		因此到达台阶5的可能就是到达台阶3,4的总和,即:f(5)=f(3)+f(4)
	 * 		类推可得:f(n)=f(n-1)+f(n-2)
	 */
public class Solution {
    public int JumpFloor(int target) {
        if(target <= 0){
            return 0;
        }else if(target == 1){    //一步到达
            return 1;
        }else if(target == 2){    //一步到位或者两个1
            return 2;
        }
        int one = 1;
        int two = 2;
        int result = 0;
        for(int i = 2;i < target; i++){    //循环求和
            result = one + two;    //前两次的和
            one =  two;        //指向下一个数
            two = result;      //指向下一个数
        }
        return result;
    }
}

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

/**
	 * 思路分析:因为每一层台阶都可以到达指定台阶
	 * 			因此:f(n)=f(1)+f(2)+...+f(n-1)+1,1是直接到达
	 * 			在分析可得:因为f(1)=1,f(2)=f(1)+1=2f(1);f(3)=f(1)+f(2)+1=2f(2)
	 * 			类推可得:f(n)=2f(n-1),即结果为2的幂次方
	 */
public class Solution {
    public int JumpFloorII(int target) {
        if(target <= 0){
            return 0;
        }else if(target == 1){
            return 1;
        }
        int result = 2;
        for(int i = 2; i< target; i++){
            result = 2 * result;    //结果为2的幂次方,每次乘以2
        }
        return result;
    }
}

10、我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法

/**
	 * 和台阶跳原理相似
	 */
public class Solution {
    public int RectCover(int target) {
        
        if(target <= 0){
            return 0;
        }else if(target == 1){
            return 1;
        }else if(target == 2){
            return 2;
        }
        
        int f1 = 1;
        int f2 = 2;
        int result = 0;
        for(int i = 2; i <target; i++){
            result = f1 + f2;
            f1 = f2;
            f2 = result;
        }
        return result;
    }
}

 


总结:剑指offer的题都是非常经典的,在此总结记录,比方便日后查看,因为有些方法的答案为博主自己编写的,所以可能不是最优答案,所以此文仅供大家参考,希望得到更优秀解答方案的可以查看剑指offer的解答。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值