Bootstrap

剑指 Offer(持续更新中)

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

思路:先找出根节点,然后利用递归方法构造二叉树

    public static class TreeNode { //构建二叉树的类
         int val;
         TreeNode left;
         TreeNode right;
         TreeNode(int x) { val = x; }
     }
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if (pre == null || in == null) {
            return null;
        }//异常处理:防止数组为空
        if (pre.length == 0 || in.length == 0) {
            return null;
        }//异常处理:防止数组长度为0
        if (pre.length != in.length) {
            return null;
        }//异常处理:防止两数组长度不等
        TreeNode root = new TreeNode(pre[0]);//实例化二叉树,并在根节点放入1
        for (int i = 0; i < pre.length; i++) {
            if (pre[0] == in[i]) {//寻找中序遍历中根节点
                root.left = reConstructBinaryTree(
                                Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));
//递归运算并将根节点的左节点抽出放入函数reConstructBinaryTree
                root.right = reConstructBinaryTree(
              Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length));
//递归运算并将根节点的右2节点抽出放入函数reConstructBinaryTree
            }
        }
        return root;
    }

8.略

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

思路:一个栈压入元素,而另一个栈作为缓冲,将栈1的元素出栈后压入栈2中。也可以将栈1中的最后一个元素直接出栈,而不用压入栈2中再出栈。

    public void push(int node) {
        stack1.push(node);
    }//将元素压入stack1

    public int pop() throws Exception {
        if (stack1.isEmpty() && stack2.isEmpty()) {
            throw new Exception("栈为空!");
        }//异常处理

        if (stack2.isEmpty()) {
            while(!stack1.isEmpty()) {
                stack2.push(stack1.pop());//对stack1进行循环,将stack1中弹出元素压入stack2中
            }
        }
        return stack2.pop();//从stack2中弹出元素
    }

10.现在要求输入一个整数n,请你输出斐波那契数列的第n项

思路:用递归的方法实现

    public long fibonacci(int n) {
      if(n<=0)
        return 0;
      if(n==1)
        return 1;
      return fibonacci(n-1)+fibonacci(n-2);
    }

思路:用循环的方法实现

public class QuickSort {
    public static void quickSort(int[] arr,int low,int high){
        int i,j,temp,t;
        if(low>high){
            return;
        }
        i=low;
        j=high;
        //temp就是基准位
        temp = arr[low];
 
        while (i<j) {
            //先看右边,依次往左递减
            while (temp<=arr[j]&&i<j) {
                j--;
            }
            //再看左边,依次往右递增
            while (temp>=arr[i]&&i<j) {
                i++;
            }
            //如果满足条件则交换
            if (i<j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }
 
        }
        //最后将基准位与i和j相等位置的数字交换
         arr[low] = arr[i];
         arr[i] = temp;
        //递归调用左半数组
        quickSort(arr, low, j-1);
        //递归调用右半数组
        quickSort(arr, j+1, high);
    }
 
 
    public static void main(String[] args){
        int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19};
        quickSort(arr, 0, arr.length-1);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

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

思路:利用二分法,找到中间的数,然后和最左边的值进行比较,若大于最左边的数,则最左边从mid开始,若小于最右边值,则最右边从mid开始。若左中右三值相等,则取mid前后值中较小的数。

    public int minNumberInRotateArray(int [] array) {
        if (array == null || array.length == 0)
            return 0;
        int left = 0;
        int right = array.length - 1;
        int mid = 0;

        while (array[left] >= array[right]) {
            if(right - left <= 1) {
                mid = right;
                break;
            }
            mid = (left + right)/2;
           if(array[left]>=array[mid])
                left=mid;
           else if(array[mid]<=array[right])
                right=mid;
        }
        return array[mid];
    }

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

思路:利用二分法,找到中间的数,然后和最左边的值进行比较,若大于最左边的数,则最左边从mid开始,若小于最右边值,则最右边从mid开始。若左中右三值相等,则取mid前后值中较小的数。

   public int minNumberInRotateArray(int [] array) {
        if (array == null || array.length == 0)
            return 0;
        int left = 0;
        int right = array.length - 1;
        int mid = 0;

        while (array[left] >= array[right]) {
           if(right - left <= 1) {
                mid = right;
                break;
           }
           mid = (left + right)/2;
           //如果下表为left,right,mid指向的数字相等,则只能顺序查找
           if(array[left]==array[right]&&array[mid]==array[left])
                return minInOrder(array,left,right)
           if(array[left]>=array[mid])
                left=mid;
           else if(array[mid]<=array[right])
                right=mid;
        }
        return array[mid];
    }
   public minInOrder(int [] array,left,right){
       int result=array[left];
        for(int i=left+1;i<=right;i++){
            if(result>array[i])
                result=array[i]
        }
   }

12.请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]

思路:回溯法、递归实现

首先,在矩阵中任选一个格子作为路径的起点。

由于回朔法的递归特性,路径可以被看成一个栈。当在矩阵中定位了路径中前n个字符的位置之后,在与第n个字符对应的格子的周围都没有找到第n+1个字符,这个时候只要在路径上回到第n-1个字符,重新定位第n个字符。一直重复这个过程,直到路径字符串上所有字符都在矩阵中找到合适的位置。

由于路径不能重复进入矩阵的格子,还需要定义和字符矩阵大小一样的布尔值矩阵,用来标识路径是否已经进入每个格子。

public class problem12 {
    public boolean exist(char[][] board,String word){
        char[] words= word.toCharArray();//将字符串转换为字符数组
        boolean res = false;
        for(int i=0;i<board.length;i++){
            for (int j = 0; j <board[0].length ; j++) {//在board中寻找起点
                res = dfs(board,words,i,j,0);//从起点开始寻找
                if(res==true)
                    return res;//存在路径
            }
        }
        return res;//不存在
    }

    private boolean dfs(char[][] board, char[] words, int i, int j, int k) {
        if(i<0 || i>=board.length || j<0 || j>=board[0].length||board[i][j]!=words[k])//遇到矩阵边界或不符合字符串返回false
            return false;
        if(k==words.length-1)
            return true;//到达字符串末尾,返回true
        board[i][j]='\0';
        boolean res = dfs(board,words,i+1,j,k+1) || dfs(board,words,i-1,j,k+1) ||
                        dfs(board,words,i,j+1,k+1) || dfs(board,words,i,j-1,k+1);
        board[i][j]=words[k];
        return res;
    }
}

13.地上有一个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 sums(int x )
    {
        int s=0;
        while(x!=0){
            s+=x%10;
            x=x/10;
        }
        return s;
    }//计算x各个位数之和

    int k,m,n;
    boolean[][] visited;//比特二维数组
    public int movingCount(int m,int n,int k){
        this.m=m;this.n=n;this.k=k;
        this.visited=new boolean[m][n];//定位二维数组大小
        return dfs(0,0);
        
    }

    private int dfs(int i, int j ){
        if(i>=m || j>=n || k<(sums(i)+sums(j)) || visited[i][j])
            return 0;
        visited[i][j]=true;//i,j可走
        return 1+dfs(i+1,j)+dfs(i,j+1);//递归运算
    }
}
import java.util.LinkedList;

public class problem13 {
    int sums(int x )
    {
        int s=0;
        while(x!=0){
            s+=x%10;
            x=x/10;
        }
        return s;
    }//返回各个位数相加之和
    public int movingCount(int m,int n,int k){
        boolean[][] visited=new boolean[m][n];//初始化二维矩阵大小
        int res =0;
        LinkedList<int[]> queue = new LinkedList<>();
        queue.add(new int[]{0,0});
        while(!queue.isEmpty()){//检验是否为空
            int[] x = queue.poll();//检索链表的第一个元素,并从列表中删除第一个元素。
            int i=x[0],j=x[1];
            if(i>=m||j>=n||k<(sums(i)+sums(j))||visited[i][j])
                continue;//不符合条件进入下一循环
            visited[i][j]=true;//{i,j}可走
            res++;//可走格数加一
            queue.add(new int[]{i+1,j});
            queue.add(new int[]{i,j+1});
        }
        return res;//返回总数
    }
    
}

14.给你一根长度为 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) {
        int[] dp = new int[n + 1];//初始化一个n+1大小的数组存放每一段绳子的大小
        dp[2] = 1;//如果绳子长度为2,结果为1
        for(int i = 3; i < n + 1; i++){//剪的第几刀
            for(int j = 2; j < i; j++){//第i刀剪出绳子的长度
                dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));//不剪和剪j孰大
            }
        }
        return dp[n];
    }
}

;