剑指offer第二版(leetcode)Java题解(不断更新)

1 数组中的重复数字

题目

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

思路

所有的数字都在0到n-1,所以可以将数字看做数组的下标。
通过把每个数字放回它的值作为索引的位置,在放置的时候判断目标位置是否已经有正确对应的值
如果没有,就交换,如果有,说明对应位置存在重复值。

代码

class Solution {
   
    public int findRepeatNumber(int[] nums) {
   
        for(int i =0;i<nums.length;i++){
   
            //如果当前数不是应该在的位置,就放过去,交换对应位置的数回来
            //如果在应该在的位置,这个就跳过去,看下一个。
            if(nums[i]!=i){
   
                int temp = nums[nums[i]];
                //移动时如果目标位置已经有了一个应该在的数,说明重复了
                if(temp == nums[i]){
   
                    return nums[i];
                }
                //没有就交换一下
                nums[nums[i]]=nums[i];
                nums[i]=temp;
                i--;
            }
        }
        return 0;
    }
}

2 二维数组中的查找

题目

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

思路

代码

class Solution {
   
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
   
        int row = matrix.length-1;
        int col = 0;
        while(row >=0&&col<matrix[0].length){
   
            if(matrix[row][col]==target)
                return true;
            if(matrix[row][col]>target){
   
                row = row -1;
            }else{
   
                col = col+1;
            }
        }
        return false;
    }
}

3 替换空格

题目

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

代码

class Solution {
   
    public String replaceSpace(String s) {
   
        if(s.length()==0)
            return s;
        int count =0;
        //求一下空格的个数确定 array的size
        for(int i= 0;i<s.length();i++)
            if(s.charAt(i) == ' ')
             count++;
        int size = s.length()+count*2;
        char [] str = new char[size];
        int j=size-1;
        //从后往前填写,其实在这道题从前往后也是一样的,从后往前在某些情境下可以实现原地
        for(int i =s.length()-1;i>=0;i--){
   
            if(s.charAt(i)==' '){
   
                str[j]='0';
                str[j-1]='2';
                str[j-2]='%';
                j=j-3;
            }else{
   
                str[j]=s.charAt(i);
                j--;
            }
        }
        return new String(str);
    }
}

4 从尾到头打印链表

题目

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
   
    //记录长度  创建返回结果 填入即可  当然 链表反转也可以解决
    public int[] reversePrint(ListNode head) {
   
        int len=0;
        ListNode cur=head;
        while(cur!=null){
   
            len++;
            cur=cur.next;
        }
        int []res=new int[len];
        for(int i=len-1;i>=0;i--){
   
            res[i]=head.val;
            head=head.next;
        }
        return res;
        
    }
}

5 重建二叉树

题目

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
   
    public TreeNode buildTree(int[] preorder, int[] inorder) {
   
        return rebuild(preorder,inorder,0,preorder.length,0,preorder.length);
    }
    public TreeNode rebuild(int [] preOrder, int []inOrder,int lp,int rp,int li,int ri){
   
        if(lp>=rp||li>=ri)
            return null;
        
        TreeNode node = new TreeNode(preOrder[lp]);
        int count = 0;
        for(int i = li;i<ri;i++){
   
            if(inOrder[i]==preOrder[lp])
                break;
            count++;
        }
        node.left = rebuild(preOrder,inOrder,lp+1,lp+1+count,li,li+count);
        node.right=rebuild(preOrder,inOrder,lp+count+1,rp,li+count+1,ri);
        return node;

    }
}

6 用两个栈实现队列

题目

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

代码

    class CQueue {
   
        public Stack<Integer> inStack;
        public Stack<Integer> outStack;
        public CQueue() {
   
            inStack= new Stack();
            outStack=new Stack();
        }

        public void appendTail(int value) {
   
            while(outStack.size()!=0){
   
                inStack.push(outStack.pop());
            }
            inStack.push(value);
        }

        public int deleteHead() {
   
            while(inStack.size()!=0){
   
                outStack.push(inStack.pop());
            }
            if(outStack.size()!=0)
                return outStack.pop();
            else
                return -1;
        }
    }

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

7 斐波那契数列

题目

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

代码

class Solution {
   
    public int fib(int n) {
   
        int first = 0;
        int second = 1;
        if(n==0)return 0;
        if(n==1)return 1;
        if(n==2)return 1;
        int third = 0;
        for(int i =2 ;i<=n;i++){
   
            third = (first+second)%1000000007;
            first = second;
            second = third;
        }
        return third;
    }
}

8 青蛙跳台阶

题目

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

代码

class Solution {
   
    public int numWays(int n) {
   
        if(n==0)return 1;
        if(n==1)return 1;
        int first = 1,second =1;
        int third = 2;
        for(int i=2;i<=n;i++){
   
            third = (first+second)%1000000007;
            first=second;
            second=third;
        }
        return third;
    }
}

9 旋转数组中的最小数字(有点东西)

题目

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

代码

class Solution {
   
    public int minArray(int[] numbers) {
   
        int left = 0,right = numbers.length-1;
        while(left+1<right){
   
            int index = left+(right-left)/2;
            if(numbers[index]>numbers[right]){
   
                left = index;
            }else if(numbers[index]<numbers[right])
                right =index;
            else
                right--;
        }
        if(numbers[left]<numbers[right])
            return numbers[left];
        else{
   
            return numbers[right];
        }
    }
}

10 矩阵中的路径

题目

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

代码

class Solution {
   
    //设定两个全局的标记
    public boolean success= false;
    public boolean[][] flag;
    public boolean exist(char[][] board, String word) {
   
        //避免一下错误输入
        if(board.length<=0||board[0].length<=0)
            return false;
        //根据输入初始化标记数组
        flag = new boolean [board.length][board[0].length];
        //所有元素作为入口
        for(int i = 0;i<board.length;i++){
   
            for(int j=0;j<board[0].length;j++){
   
                move(board,i,j,word,0);
            }
        }
        return success;
    }
    public void move(char [][]board,int i,int j,String word, int index){
   
        //有一个成功就不继续了
        if(success == true)
            return ;
        //走完word就是成功
        if(index == word.length()){
   
            success = true;
            return ;
        }
        //越界情况处理掉
        if(i<0||i>=board.length||j<0||j>=board[0].length)
            return;
        //如果flag表示没走过这个点且这个点和word当前字符相同,那就往上下左右继续走
        if(flag[i][j]==false&&board[i][j]==word.charAt(index)){
   
            flag[i][j]=true;
            move(board,i+1,j,word,index+1);
            move(board,i-1,j,word,index+1);
            move(board,i,j+1,word,index+1);
            move(board,i,j-1,word,index+1);
            //走完说明上下左右没有匹配到,这个点标记恢复标记,退回上一步的节点继续走
            flag[i][j]=false;
        }
    }
}

11 机器人的运动范围

题目

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

代码

class Solution {
   
    //要考虑好最重要的就是走过的路能不能走的问题,实际上遍历就是不能往回走的,但是一样可以遍历到所有的路,还是深度优先遍历没有理解好
    int count = 0;
    boolean [][]flag;
    public int movingCount(int m, int n, int k) {
   
        //初始化一个标记数组
        flag = new boolean[m][n];
        move(m,n,k,0,0);
        return count;
    }
    public void move(int m,int n,int k,int x,int y){
   
        //越界不继续
        if(x<0||x>=m||y<0||y>=n)
            return ;
        //走过不走
        if(flag[x][y]==true){
   
            return;
        }
        //如果符合要求就走进来,然后上下左右。
        if(k>=count(x,y)){
   
            flag[x][y]=true;
            count++;
            move(m,n,k,x+1,y);
            move(m,n,k,x-1,y);
            move(m,n,k,x,y-1);
            move(m,n,k,x,y+1);
        }
    }
    //实际的业务要求独立出来
    public int count(int x,int y){
   
        int sum =0;
        while(x>0){
   
            sum+=x%10;
            x=x/10;
        }
        while(y>0){
   
            sum+=y%10;
            y=y/10;
        }
        return sum;
    }
}

12 剪绳子I

题目

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

代码

class Solution {
   
    public int cuttingRope(int n) {
   
        //简单数学证明一下切3 切2 的大小比较
        if(n==2)return 1;
        if(n==3)return 2;
        if(n==4)return n;
        int sum =1;
        while(n>=5){
   
            sum=sum*3;
            n=n-3;
        }
        if(n>0){
   
            sum*=n;
        }
        return sum;
    }
}

13 剪绳子 II

题目

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

代码

class Solution {
   
    public int cuttingRope(int n) {
   
        //简单证明一下切3 切2 的大小比较
        if(n==2)return 1;
        if(n==3)return 2;
        if(n==4)return n;
        //这里防止溢出
        long sum =1;
        while(n>=5){
   
            //这里乘三有溢出的情况,所以取余也莫得用
            sum=sum*3%1000000007;
            n=n-3;
        }
        if(n>0){
   
            sum=sum*n%1000000007;
        }
        return (int)sum;
    }
}

14 二进制中1的个数

题目

请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

代码

public class Solution {
   
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
   
        //n是整数,但是负整数也是整数
        //仗着java有无符号右移,所以这个问题被掩盖了        int count =0;
        while(n!=0){
   
            if((n&1)==1){
   
                count++;
            }
            n=n>>>1;
        }
        return count;
    }
}
//如果不用无符号右移
public class Solution {
   
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
   
        //n是整数,但是负整数也是整数
        //仗着java有无符号右移,所以这个问题被掩盖了
        int count =0;
        for(int i=0;i<32;i++){
   
            if((n&1)==1){
   
                count++;
            }
            n=n>>1;
        }
        return count;
    }
}

15 数值的整数次方

题目

实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

代码

class Solution {
   
    public double myPow(double x, int n) {
   
        if(n==0)return 1
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值