Leetcode [ 剑指offer ]

 

目录

剑指 Offer 03. 数组中重复的数字

剑指 Offer 04. 二维数组中的查找

剑指 Offer 05. 替换空格

剑指 Offer 06. 从尾到头打印链表

剑指 Offer 07. 重建二叉树

剑指 Offer 09. 用两个栈实现队列

剑指 Offer 10- I. 斐波那契数列

剑指 Offer 10- II. 青蛙跳台阶问题

剑指 Offer 11. 旋转数组的最小数字

剑指 Offer 12. 矩阵中的路径

剑指 Offer 13. 机器人的运动范围

剑指 Offer 14- I. 剪绳子

剑指 Offer 14- II. 剪绳子 II

剑指 Offer 15. 二进制中1的个数

剑指 Offer 16. 数值的整数次方

剑指 Offer 17. 打印从1到最大的n位数

剑指 Offer 18. 删除链表的节点

剑指 Offer 19. 正则表达式匹配

剑指 Offer 20. 表示数值的字符串

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

剑指 Offer 22. 链表中倒数第k个节点

剑指 Offer 24. 反转链表

剑指 Offer 25. 合并两个排序的链表

剑指 Offer 26. 树的子结构

剑指 Offer 27. 二叉树的镜像

剑指 Offer 28. 对称的二叉树

剑指 Offer 29. 顺时针打印矩阵

剑指 Offer 30. 包含min函数的栈

剑指 Offer 31. 栈的压入、弹出序列


剑指 Offer 03. 数组中重复的数字

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

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

//方法一:hash表 
class Solution {
    public int findRepeatNumber(int[] nums) {
        // set中没有重复的元素
        Set<Integer> set = new HashSet<Integer>();
        int res = -1;
        for(int num :nums){
            if(!set.add(num)){    //add失败,找到了某个重复元素
                res = num;
                break;
            } 
        }
        return res; //没有找到重复的元素返回-1
    }
}

//方法二:
class Solution {
    public void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
    public int findRepeatNumber(int[] nums) {
        int n = nums.length;
        //1~ n-1
        for(int num : nums){
            if(num < 0 || num > n - 1){
                return -1;
            }
        }
        //利用下标交换,保证下标 == 元素值
        for(int i = 0;i < n;i++){
            while(nums[i] != i && nums[nums[i]] != nums[i]){
                swap(nums, i, nums[i]); //交换nums[i] nums[nums[i]]
            }
            if(nums[i] != i && nums[nums[i]] == nums[i])
                return nums[i];
            }
        return -1; //没有找到重复的元素返回-1
    }
}

//方法三:排序
class Solution {
    public int findRepeatNumber(int[] nums) {
        Array.sort(nums);
        for(int i = 0; i < nums.length - 1;i++){
            if(nums[i] == nums[i + 1])
            return nums[i];
        }
        return -1;
    }
}

剑指 Offer 04. 二维数组中的查找

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

示例:

现有矩阵 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        //判断数组空
        if((matrix == null || matrix.length ==0) || (matrix.length ==1 && matrix[0].length ==0))
            return false;
        int i = 0; int j = matrix[0].length - 1;//右上角
        while(i <= matrix.length -1 && j >=0){
            //一定要先判断是否相等
            if(target == matrix[i][j]) return true;
            if(target < matrix[i][j]) j--;
            if(target > matrix[i][j]) i++; 
        }
        return false;
    }
}

剑指 Offer 05. 替换空格

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

示例 1:

输入:s = "We are happy."
输出:"We%20are%20happy."
//方法一:
class Solution {
    public String replaceSpace(String s) {
        int n = s.length();
        char[] array = new char[n * 3];
        int size = 0;
        for(int i = 0; i < n; i++){
            char c = s.charAt(i);
            if(c == ' '){
                array[size ++] = '%';
                array[size ++] = '2';
                array[size ++] = '0';
            }
            else{
                array[size ++] = c;
            }
        }
        String news = new String(array, 0, size);
        return news;
    }
}

//方法二
class Solution {
    public String replaceSpace(String s) {
        return s.replace(" ","%20");
    }
}

剑指 Offer 06. 从尾到头打印链表

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

示例 1:

输入:head = [1,3,2]
输出:[2,3,1]

 

//方法一:栈
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        //压栈
        Stack<ListNode> stk = new Stack<ListNode>();
        ListNode temp = head;
        while(temp != null) //一直指向最后一个节点
        {
            stk.push(temp);
            temp = temp.next;
        }
        int size = stk.size();
        int[] res = new int[stk.size()];
        for(int i = 0; i < size; i++){
            res[i] = stk.pop().val;
        }
        return res;
    }
}

//方法二:
class Solution {
    public int[] reversePrint(ListNode head) {
        ListNode temp = head;
        int size = 0;
        while(temp != null){
            size++;
            temp = temp.next;
        }
        int res[] = new int[size];
        temp = head;
        for(int i = size - 1; i >= 0; i--){
            res[i] = temp.val;
            temp = temp.next;
        }


        return res;
    }
}

剑指 Offer 07. 重建二叉树

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

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

/**
 * 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) {
        // 前序遍历
        // (preLIndex)根 (preLIndex+1)左子树(preLIndex+pIndex-inLIndex) (preLIndex+pIndex-inLIndex)右子树(preRIndex)

        // 中序遍历
        // (inLIndex)左子树(pIndex-1) (pIndex)根 (pIndex+1)右子树(inRIndex) 

        //将中序遍历数组中的值放入map中,这样不需要遍历,就能找到根节点。
        Map<Integer,Integer> map = new HashMap();
        for(int i = 0; i < inorder.length; i++){
            map.put(inorder[i],i);
        }
        return buildTree(preorder,0,preorder.length-1,inorder,0,inorder.length-1,map);
    }

    //递归
    private TreeNode buildTree(int[] preorder,int preLIndex,int preRIndex,int[] inorder,int inLIndex,int inRIndex,Map<Integer,Integer> map){
        if(preLIndex > preRIndex || inLIndex > inRIndex){
            return null;
        }
        int pIndex = map.get(preorder[preLIndex]); //拿到中序遍历的根坐标
        TreeNode root = new TreeNode(preorder[preLIndex]); // 根节点,前序遍历中的第一个值
        //构建左子树
        root.left = buildTree(preorder,preLIndex+1,preLIndex+pIndex-inLIndex,inorder,inLIndex,pIndex-1,map);
        //构建右子树
        root.right = buildTree(preorder,preLIndex+pIndex-inLIndex+1,preRIndex,inorder,pIndex+1,inRIndex,map);
        return root;
    }

}

剑指 Offer 09. 用两个栈实现队列

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

示例 1:

输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:

输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]

class CQueue {
    private Stack<Integer> stack1;
    private Stack<Integer> stack2;

    public CQueue() {
        stack1 = new Stack();
        stack2 = new Stack();
    }
    
    public void appendTail(int value) {
        stack1.push(value);
    }
    
    public int deleteHead() {
        if(stack1.isEmpty() && stack2.isEmpty()){
            return -1;
        }else{
            if(stack2.isEmpty()){
                while(!stack1.isEmpty()){
                    stack2.push(stack1.pop());
                }
            }
        }
        return stack2.pop();
    }
}

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

剑指 Offer 10- I. 斐波那契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

示例 1:

输入:n = 2
输出:1

示例 2:

输入:n = 5
输出:5
class Solution {
    public int fib(int n) {
        int f0 = 0;
        int f1 = 1;
        if(n == 0){
            return f0;
        }
        if(n == 1){
            return f1;
        }
        int fn = 0;
        for(int i = 0; i < n - 1; i++){
            fn = (f0 + f1)%1000000007;
            f0 = f1;
            f1 = fn;
        }
        return fn;
    }
}

剑指 Offer 10- II. 青蛙跳台阶问题

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

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

示例 1:

输入:n = 2
输出:2

/*第0级:一种方式,就是原地不动;

第1级:1种方式,即从0走一步到第1级;

第2级:2种方式,即从0到1到2,或者直接从0到2;

第3级:考虑每次只能上1或者2级,那么到第三级只有两种情况:从第1级上两步到第3级,或者从第2级走一步
到第三级。上面我们又计算了,到第1级只有一种方式,到第二级有2种方式,所以到第三级的方式就有:
1*1+2*1=3。每一次从前一级或者前二级到当前级都只有一种方式,所以也可以写成:1+2=3。

... ...

第n级:同上,到第n级只有两种方式:从n-2级走两步到第n级,或者从n-1级走一步到第n级。假设到第n-2级
的方式有 f(n-2)种,到第n-1级的方式有f(n-1)种,则到第n级的方式有:
f(n-2)*1+f(n-1)*1=f(n-2)+f(n-1)。
*/

class Solution {
    public int numWays(int n) {
        int f1 = 1;
        int f2 = 2;
        if(n == 0 || n == 1){
            return 1;
        }
        if(n == 2){
            return 2;
        }
        int fn = 0;
        //1 1 2 3
        for(int i = 1; i < n - 1; i++){
            fn = (f1 + f2)%1000000007; //一,八个零,七
            f1 = f2;
            f2 = fn;
        }
        return fn;
    }

}

剑指 Offer 11. 旋转数组的最小数字

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

示例 1:

输入:[3,4,5,1,2]
输出:1

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

剑指 Offer 12. 矩阵中的路径

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true

class Solution {
    public boolean exist(char[][] board, String word) {
        if(board == null || board.length == 0 || board[0].length == 0){
            return false;
        }

        char[] chars = word.toCharArray(); //转换为字符数组
        boolean[][] vis = new boolean[board.length][board[0].length]; //记录是否遍历过
        for(int i = 0; i < board.length; i++){
            for(int j = 0;j < board[0].length; j++){
                // 从(0,0)开始进行dfs
                // 如果以(0,0)点没有对应的路径,那么就从(0,1)去找
                if(dfs(board,chars,vis,i,j,0)){
                    return true;
                }
            }
        }
        return false;
    }
    
    private boolean dfs(char[][] board, char[] chars, boolean[][] vis, int i, int j, int start){
        if(i < 0 || i >= board.length || j < 0 || j >= board[0].length || chars[start] != board[i][j] || vis[i][j]){
            return false;
        }
        if(start == chars.length - 1){
            return true;
        }
        vis[i][j] = true;
        boolean ans = false;
        ans = dfs(board,chars,vis,i + 1,j,start + 1) || dfs(board,chars,vis,i - 1,j,start + 1) || dfs(board,chars,vis,i,j + 1,start + 1) || dfs(board,chars,vis,i,j - 1,start + 1);
        vis[i][j] = false;
        return ans;
    }
}

剑指 Offer 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。请问该机器人能够到达多少个格子?

示例 1:

输入:m = 2, n = 3, k = 1
输出:3
class Solution {
    public int movingCount(int m, int n, int k) {
        //深搜
        //记录是否走过
        boolean[][] vis = new boolean[m][n];  
        return dfs(0, 0, m, n, k, vis);
    }

    private int dfs(int x, int y, int m, int n, int k, boolean[][] vis){
        if(x < 0 || y < 0|| x > m - 1 || y > n - 1 || x / 10 + x % 10 + y / 10 + y % 10 > k || vis[x][y]){
            return 0;
        }
        vis[x][y] =true;
        return dfs(x - 1, y, m, n, k, vis) + dfs(x + 1, y, m, n, k, vis) + dfs(x, y + 1, m, n, k, vis)+ dfs(x, y - 1, m, n, k,vis) + 1;
    }
}

剑指 Offer 14- 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。

示例 1:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

  • 2 <= n <= 58
//动态规划
class Solution {
    public int cuttingRope(int n) {
        // n>3时,所有数都可以是2、3的组合后乘积,max(dp[n-x]*dp[x]) dp[n] 实现最大值的动态转移
        if(n < 2){
            return 1;
        }        
        if(n == 2){
            return 1;
        }
        if(n == 3){
            return 2;
        }
        int[] dp = new int[n + 1];
        // >=4的值,2、3不切分
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        for(int i = 4; i <= n; i++){
            for(int j =1; j <= i/2; j++){ //j是在哪里分割
                dp[i] = Math.max(dp[j] * dp[i - j], dp[i]);
            }
        }
        return dp[n];
    }
}

剑指 Offer 14- II. 剪绳子 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。

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

  • 2 <= n <= 1000
// 贪心算法
// 3越多,乘积越大
class Solution {
    public int cuttingRope(int n) {
        if(n == 2){
            return 1;
        }
        if(n == 3){
            return 2;
        }
        if(n == 4){
            return 4;
        }
        long res = 1;
        while(n > 4){
            res *= 3;
            res = res % 1000000007;
            n -= 3;
        }
        return (int)(res * n % 1000000007);
    }
}

剑指 Offer 15. 二进制中1的个数

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

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

// 与运算,int是32位
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res = 0;
        for(int i = 0; i < 32; i++){
            res += n & 1;
            n = n >>> 1;
        }
        return res;
    }
}

剑指 Offer 16. 数值的整数次方

实现 pow(xn) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000

class Solution {
    //快速幂
    //例如:求a^11  
    //11 --> 1011
    //11 = 1*2^3+0*2^2+1*2^1+1*2^0;
    //a^11 = a^(2^0+2^1+2^3)
    //不用再遍历11次,而是遍历3次就可以
    public double myPow(double x, int n) {
        long b = n;
        if(n < 0){
            b = -b;
            x = 1/x;
        }
        double res = 1.0;
        while(b != 0){        //1011 = 2^0+2^1+2^3
            if((b & 1) == 1){
                res *= x;    // res = a, a*a^2, a*a^2, a*a^2*a^8
            }
            b >>= 1;         // 0101, 0010, 0001, 0000
            x*=x;            // x = a^2, a^4, a^8, a^16
        }
        return res;         //a*a^2*a^8
    }
}

剑指 Offer 17. 打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

示例 1:

输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
class Solution {
    public int[] printNumbers(int n) {
        //求10^n - 1的值
        //快速幂
        int x = 10; //底数
        int res = 1;
        while(n != 0){
            if((n & 1) == 1){
                res *= x;
            }
            x *= x;
            n >>= 1;
        }
        int[] resArr = new int[res - 1];
        for(int i = 0; i < resArr.length; i++){
            resArr[i] = i + 1;
        }
        return resArr;
    }
}

剑指 Offer 18. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        //如果头结点就是要找的值,那么直接摘链就可以了
        if(head.val == val){
            head = head.next;
        }
        //否则
        ListNode temp = head;
        ListNode pre = null;
        while(temp != null){
            if(temp.val == val){
                pre.next = temp.next; // 摘链
            }
            pre = temp;
            temp = temp.next;
        }
        return head;
    }
}

剑指 Offer 19. 正则表达式匹配

请实现一个函数用来匹配包含'. '和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。

示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:

输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:

输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。

//方法一:递归
class Solution {
    public boolean isMatch(String s, String p) { //s为字符串,p为正则表达式
        if(p.isEmpty()){ //如果正则表达式为空
            return s.isEmpty(); //s也为空,则匹配成功
        }
        //记录第一位是否相等
        boolean headMatch = !s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.');
        //如果下一个节点是“*”
        //s = ac*a; p = b*ac*a; p = ac*a
        //s = bb*ac*a; p = b*ac*a; s = b*ac*a;
        if(p.length() >= 2 && p.charAt(1) == '*'){
            // 截掉p || 截掉s
            return isMatch(s,p.substring(2)) || (headMatch && isMatch(s.substring(1),p));

        }else if(headMatch){
            return isMatch(s.substring(1),p.substring(1));
        }else{
            return false;
        }
    }
}

//方法二:动态规划

class Solution {
    int[][] dp; //dp[i][j] s.substring(i) p.substring(j) 是否匹配,匹配1,不匹配0
    String s;
    String p;
    int n,m; // n代表s的长度,m代表p的长度
    public boolean isMatch(String s, String p) { //s为字符串,p为正则表达式
        n = s.length();
        m = p.length();
        this.s = s;
        this.p = p;
        dp = new int[n + 1][m + 1];
        for(int i = 0; i <= n; i++){
            Arrays.fill(dp[i], -1); //先将数组值全赋值为-1
        }
        return solve(0,0) == 1;
    }
    private int solve(int i, int j){
        if(i == n && j == m) return 1; // n = 0, m = 0,说明为两个空串,相等
        if(j == m) return 0; // 没有完全匹配原串,p为空串
        if(dp[i][j] != -1) return dp[i][j]; //说明已经匹配过了

        int res = 0;
        if(j + 1 < m && p.charAt(j + 1) == '*'){
            res = solve(i, j + 2); // 包含0次
            for(int k = i; k < n && res != 1; k++){
                if(s.charAt(k) != p.charAt(j) && p.charAt(j) != '.') break;
                res = solve(k + 1,j + 2);
            }
        }else if(i != n){
            if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'){
                res = solve(i + 1,j +1 );
            }
        }
        return dp[i][j] = res;
    }
}

剑指 Offer 20. 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

数值(按顺序)可以分成以下几个部分:

若干空格
一个 小数 或者 整数
(可选)一个 'e' 或 'E' ,后面跟着一个 整数
若干空格
小数(按顺序)可以分成以下几个部分:

(可选)一个符号字符('+' 或 '-')
下述格式之一:
至少一位数字,后面跟着一个点 '.'
至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
一个点 '.' ,后面跟着至少一位数字
整数(按顺序)可以分成以下几个部分:

(可选)一个符号字符('+' 或 '-')
至少一位数字
部分数值列举如下:

["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]
部分非数值列举如下:

["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]

示例 1:

输入:s = "0"
输出:true
示例 2:

输入:s = "e"
输出:false
示例 3:

输入:s = "."
输出:false
示例 4:

输入:s = "    .1  "
输出:true

class Solution {
    public boolean isNumber(String s) {
        // .之前不能出现. 或 e
        // e之前不能出现e,且必须出现数字
        // +-出现在0位置或者e/E的后面第一个位置才是合法的

        boolean seeNum = false;
        boolean seeDot = false;
        boolean seeE = false;
        s = s.trim();
        for(int i = 0; i < s.length(); i++){
            char c = s.charAt(i);
            if(c >= '0' && c <= '9'){
                seeNum = true;
            }else if(c == '.'){
                if(seeDot || seeE){
                    return false;
                }
                seeDot = true;
            }else if(c == 'e' || c == 'E'){
                if(seeE || !seeNum){
                    return false;
                }
                seeE = true;
                seeNum = false; // 避免出现12e这种情况
            }else if(c == '-' || c == '+'){
                if(i != 0 && s.charAt(i - 1) != 'e' && s.charAt(i - 1) != 'E'){
                    return false;
                }
            }else{
                return false;
            }
        }
        return seeNum;

    }
}

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。
class Solution {
    public int[] exchange(int[] nums) {
        //双指针
        int left = 0;
        int right = nums.length - 1;
        while(left < right){
            while(left < right && nums[left] % 2 == 1){
                left++;
            }
            while(left < right && nums[right] % 2 == 0){
                right--;
            }
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
        }
        return nums;
    }
}

剑指 Offer 22. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.
//head指向的是头结点 即第一个点
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
       // 双指针
        ListNode first = head;
        ListNode second = head;
        for(int i = 0; i < k; i++){
            first = first.next;
        }
        while(first != null){
            first = first.next;
            second = second.next;
        }
        return second;
    }
}

剑指 Offer 24. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
// 单向链表
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode temp = curr.next; //保存下一个节点
            curr.next = prev; //当前节点指向前一个节点
            prev = curr; //当前为下一轮的前一个节点
            curr = temp; // curr修改为下一轮的当前节点
        }
        return prev;
    }
}

剑指 Offer 25. 合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

示例1:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(0);
        ListNode cur = res;
        while(l1 != null && l2 != null){
            if(l1.val < l2.val ){
                cur.next = l1;
                cur = cur.next;
                l1 = l1.next;
            }else{
                cur.next = l2;
                cur = cur.next;
                l2 = l2.next;
            }
        }
        if(l1 != null){
            cur.next = l1;
        }else if(l2 != null){
            cur.next = l2;
        }
        return res.next;
    }
}

剑指 Offer 26. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
给定的树 A:

     3
    / \
   4   5
  / \
 1   2
给定的树 B:

   4 
  /
 1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:

输入:A = [1,2,3], B = [3,1]
输出:false
示例 2:

输入:A = [3,4,5,1,2], B = [4,1]
输出:true

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
 // 一般树的题目都是用递归解决
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A == null || B == null){
            return false;
        }
        return isSubTree(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
    }
    private boolean isSubTree(TreeNode A, TreeNode B){
        if(B == null){ //B已经遍历结束
            return true;
        }
        if(A == null || A.val != B.val){ //A已经nullB还未匹配完;AB值不同
            return false;
        }
        return isSubTree(A.left,B.left) && isSubTree(A.right,B.right);
    }
}

剑指 Offer 27. 二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

例如输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9

镜像输出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

示例 1:

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

限制:

0 <= 节点个数 <= 1000

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode temp = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(temp);
        return root;

    }
}

剑指 Offer 28. 对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null){
            return true;
        }
        return isMirrorTree(root.left,root.right);
    }

    private boolean isMirrorTree(TreeNode left,TreeNode right){
        if(left == null && right == null){
            return true;
        }
        if(left == null || right == null || left.val != right.val){
            return false;
        }
        return isMirrorTree(left.right,right.left) && isMirrorTree(left.left,right.right);
    }
}

剑指 Offer 29. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        if(matrix.length ==0 ){
            return new int[]{}; //写成int[][]是错的!!
        }
        int left = 0;
        int right = matrix[0].length - 1;
        int top = 0;
        int bottom = matrix.length -1;
        int[] res = new int[(right + 1) * (bottom + 1)];
        int x = 0;
        while(true){
            //向左
            for(int i = left; i <= right; i++){
                res[x] = matrix[top][i];
                x++;
            }
            if(++top > bottom){
                break;
            }
            //向下
            for(int i = top; i <= bottom; i++){
                res[x] = matrix[i][right];
                x++;
            }
            if(--right < left){
                break;
            }
            //向左
            for(int i = right; i >= left; i--){
                res[x] = matrix[bottom][i];
                x++;
            }
            if(--bottom < top ){
                break;
            }
            //向上
            for(int i = bottom; i >= top; i--){
                res[x] = matrix[i][left];
                x++;
            }
            if(++left > right){
                break;
            }
        }
        return res;
    }
}

剑指 Offer 30. 包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.min();   --> 返回 -2.

class MinStack {
    private Stack<Integer> a;
    private Stack<Integer> b;
    /** initialize your data structure here. */
    public MinStack() {
        a = new Stack();
        b = new Stack();
    }
    
    public void push(int x) {
        a.push(x);
        if(b.isEmpty() || b.peek() >= x){
            b.push(x);
        }
    }
    
    public void pop() {
        //此题如果用==将会无法通过 Integer的equals重写过,
        //比较的是内部value的值, ==如果在[-128,127]会被cache缓存
        //,超过这个范围则比较的是对象是否相同
        if(b.peek().equals(a.pop())){ 
            b.pop();
        }
    }
    
    public int top() {
        return a.peek();
    }
    
    public int min() {
        return b.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

剑指 Offer 31. 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        if(pushed.length != popped.length) return false;
        if(pushed.length < 1) return true; // 入栈是空,则出栈也是空
        Stack<Integer> stack = new Stack();
        stack.push(pushed[0]);
        int i = 1;
        for(int j = 0; j < popped.length; j++){
            int num = popped[j];

            // 一开始这里没有加stack.isEmpty()
            // 输入[0,1][0,1]时报错,栈为空
            while(stack.isEmpty() || (stack.peek() != num && i < pushed.length)){
                stack.push(pushed[i++]);
            }
            if(stack.peek() == num){
                stack.pop();
                continue;
            }
            return false;
        }
        return true;
    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值