剑指offer

47.在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

示例 1:

输入: 
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
 

提示:

0 < grid.length <= 200
0 < grid[0].length <= 200

修改数组搞一哈

class Solution {
    public int maxValue(int[][] grid) {
        int m=grid.length;
        int n=grid[0].length;
        for(int i=1;i<m;i++){
            grid[i][0]+=grid[i-1][0];
        }
         for(int i=1;i<n;i++){
            grid[0][i]+=grid[0][i-1];
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                grid[i][j]=Math.max(grid[i-1][j],grid[i][j-1])+grid[i][j];
            }
        }
        return grid[m-1][n-1];
    }
}

 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

注意:本题与主站 226 题相同:https://leetcode-cn.com/problems/invert-binary-tree/

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        //递归
        TreeNode temp=root;
        if(temp==null){
            return root;
        }
        //否则交换左右子树
        TreeNode t=temp.left;
        temp.left=temp.right;
        temp.right=t;
        mirrorTree(temp.left);
        mirrorTree(temp.right);
        return root;
    }
}

搞个辅助栈看看。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        Stack<TreeNode> stack=new Stack<>();
        if(root==null){
            return null;
        }
        TreeNode temp=root;    
        stack.push(temp);
        while(!stack.isEmpty()){
            //先弹出一个当前结点
            TreeNode t=stack.pop();
            //压入当前结点的左右节点
            if(t.right!=null){
                stack.push(t.right);
            }
            if(t.left!=null){
                stack.push(t.left);
            }
            //将当前节点的左右节点交换
            TreeNode n=t.left;
            t.left=t.right;
            t.right=n;
        }
        return root;
    }
}

主要过程就是先压根节点,每个结点都被访问到,对每个结点的左右节点进行交换,每个结点是通过栈顶访问的。

 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
 

限制:

0 <= 节点个数 <= 1000

注意:本题与主站 101 题相同:https://leetcode-cn.com/problems/symmetric-tree/

迭代双栈

class Solution {
    public boolean isSymmetric(TreeNode root) {
        //迭代两个栈
        TreeNode temp=root;
        if(temp==null){
            return true;
        }
        Stack<TreeNode> left=new Stack<>();
        Stack<TreeNode> right=new Stack<>();
        left.push(temp);
        right.push(temp);
        while(!left.isEmpty()&&!right.isEmpty()){
            TreeNode t1=left.pop();
            TreeNode t2=right.pop();
            if(t1.val!=t2.val){
                return false;
            }
            if(t1.left!=null&&t2.right!=null){
                left.push(t1.left);
                right.push(t2.right);
            }else if(t1.left==null&&t2.right==null){

            }else{
                return false;
            }
            if(t1.right!=null&&t2.left!=null){
                left.push(t1.right);
                right.push(t2.left);
            }else if(t1.right==null&&t2.left==null){

            }else{
                return false;
            }

        }
        if(left.isEmpty()&&right.isEmpty()){
            return true;
        }else{
            return false;
        }

    }
}

 递归还是牛的

class Solution {
    public boolean isSymmetric(TreeNode root) {
        //递归
        if(root==null){
            return true;
        }else{
            return func(root.left,root.right);
        }

    }
    public boolean func(TreeNode l,TreeNode r){
        if(r==null&&l==null){
            return true;
        }else if(l!=null&&r!=null&&l.val==r.val){
            return func(l.left,r.right)&&func(l.right,r.left);
        }else{
            return false;
        }
    }
}

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]
 

限制:

0 <= matrix.length <= 100
0 <= matrix[i].length <= 100
注意:本题与主站 54 题相同:https://leetcode-cn.com/problems/spiral-matrix/

 嘿嘿 自己写出来了耶

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        int m=matrix.length;
         if(m==0){
             return new int[0];
        }
        int n=matrix[0].length;
        int[] res=new int[m*n];
        boolean[][] flag=new boolean[m][n];
        int x=0;
        int y=0;
        int index=0;
        res[0]=matrix[0][0];
        flag[0][0]=true;
        //在上下左右都行不通的时候,退出循环
        while(index!=m*n-1){
            while(y+1<n&&flag[x][y+1]!=true){
                y++;
                index++;
                flag[x][y]=true;
                res[index]=matrix[x][y];
            }
            while(x+1<m&&flag[x+1][y]!=true){
                x++;
                index++;
                flag[x][y]=true;
                res[index]=matrix[x][y];
            }
            while(y-1>=0&&flag[x][y-1]!=true){
                y--;
                index++;
                flag[x][y]=true;
                res[index]=matrix[x][y];
            }
            while(x-1>=0&&flag[x-1][y]!=true){
                x--;
                index++;
                flag[x][y]=true;
                res[index]=matrix[x][y];
            }
        }
        return res;
    }
}

有没有一种可能不需要额外的空间呢?

一层一层的打印?

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return new int[0];
        }
        int rows = matrix.length, columns = matrix[0].length;
        int[] order = new int[rows * columns];
        int index = 0;
        int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
        while (left <= right && top <= bottom) {
            for (int column = left; column <= right; column++) {
                order[index++] = matrix[top][column];
            }
            for (int row = top + 1; row <= bottom; row++) {
                order[index++] = matrix[row][right];
            }
            if (left < right && top < bottom) {
                for (int column = right - 1; column > left; column--) {
                    order[index++] = matrix[bottom][column];
                }
                for (int row = bottom; row > top; row--) {
                    order[index++] = matrix[row][left];
                }
            }
            left++;
            right--;
            top++;
            bottom--;
        }
        return order;
    }
}

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) {
        //动态规划,f[i][j]表示s的前i个字符可以与p的前j个字符是否相匹配
        //如果p[j]是普通字符,且s[i]==p[j],那么f[i][j]=f[i-1][j-1]
        //如果P[j]是.,那么f[i][j]=f[i-1][j-1]
        //如果p[j]是*,如果P[j-1]==s[i],f[i][j]=f[i][j-2]||f[i-1][j],如果不匹配,f[i][j]=f[i][j-2]
        int m=s.length();
        int n=p.length();
        //第i个实际上是在字符串里是i-1,但boolean数组人为加了一个0,0,所以里面是i
        boolean[][] res=new boolean[m+1][n+1];
        res[0][0]=true;
        for(int i=0;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(p.charAt(j-1)=='*'){
                    if(match(s,p,i,j-1)){
                        //又忘了!!也有可能是多余的,如果多余可以,不必退
                        res[i][j]=res[i][j-2]||res[i-1][j];
                    }else{
                        res[i][j]=res[i][j-2];
                    }
                }else{
                    if(match(s,p,i,j)){
                        res[i][j]=res[i-1][j-1];
                    }
                }
            }
        }
        return res[m][n];
    }
    public boolean match(String s,String p,int i,int j){
        //其实没有第0个字符
        if(i==0){
            return false;
        }
        if(p.charAt(j-1)=='.'){
            return true;
        }
        return s.charAt(i-1)==p.charAt(j-1);
    }
}

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

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

例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。

示例 1:

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

输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false
 

提示:

1 <= board.length <= 200
1 <= board[i].length <= 200
board 和 word 仅由大小写英文字母组成
 

注意:本题与主站 79 题相同:https://leetcode-cn.com/problems/word-search/

注意点在于:必须把k==length写在最前面!!! 

class Solution {
    public boolean exist(char[][] board, String word) {
        //回溯
        int m=board.length;
        int n=board[0].length;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(check(board,word,i,j,0)){
                    return true;
                }
            }
        }
        return false;
    }
    public boolean check(char[][] board,String word,int x,int y,int k){
        int m=board.length;
        int n=board[0].length;
        int length=word.length();
        if(k==length){
            return true;
        }
        if(x<0||x>=m||y<0||y>=n){
            return false;
        }
        
        if(board[x][y]!=word.charAt(k)){
            return false;
        }
        char temp=board[x][y];
        board[x][y]='0';
        boolean res=check(board,word,x-1,y,k+1)||check(board,word,x+1,y,k+1)||check(board,word,x,y+1,k+1)||check(board,word,x,y-1,k+1);
        board[x][y]=temp;
        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。

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

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:

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

提示:

2 <= n <= 1000
注意:本题与主站 343 题相同:https://leetcode-cn.com/problems/integer-break/

注意越界问题,数学解法:

class Solution {
    public int cuttingRope(int n) {
        if(n==2){return 1;}
        if(n==3){return 2;}
        int k=n/3;
        int a=n%3;
        if(a==1){
            k--;
            a+=3;
        }else if(a==0){
            a=1;
        }
        //直接存储结果double也会越界
        long res=1;
        while(k!=0){
            res*=3;
            k--;
            if(res>1000000007){
                res%=1000000007;
            }
        }
        res*=a;
        if(res>1000000007){
            res%=1000000007;
        }
        return (int)res;
    }
}

 优化求结果部分,使用位运算?

class Solution {
    public int cuttingRope(int n) {
        if(n==2){return 1;}
        if(n==3){return 2;}
        int k=n/3;
        int a=n%3;
        if(a==1){
            k--;
            a+=3;
        }else if(a==0){
            a=1;
        }
        //直接存储结果double也会越界
        long res=1;
        long temp=3;
        while(k!=0){
            // res*=3;
            // k--;
            // if(res>1000000007){
            //     res%=1000000007;
            // }
            //进行优化
            if((k&1)==1){
                res*=temp;
                res%=1000000007;
            }
            temp*=temp;
            temp%=1000000007;
            k>>=1;
        }
        res*=a;
        if(res>1000000007){
            res%=1000000007;
        }
        return (int)res;
    }
}

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {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
示例 2:

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。
 

提示:

0 <= pushed.length == popped.length <= 1000
0 <= pushed[i], popped[i] < 1000
pushed 是 popped 的排列。
注意:本题与主站 946 题相同:https://leetcode-cn.com/problems/validate-stack-sequences/

 用栈来模拟,遍历push数组,只要栈顶元素和pop当前元素相等,就弹出,最后判断栈是否为空。

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> stack=new Stack<>();
        int index=0;
        for(int p:pushed){
            stack.push(p);
            while(!stack.isEmpty()&&stack.peek()==popped[index]){
                stack.pop();
                index++;
            }
        }
        return stack.isEmpty();
    }
}

49.我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

示例:

输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:  

1 是丑数。
n 不超过1690。
注意:本题与主站 264 题相同:https://leetcode-cn.com/problems/ugly-number-ii/

class Solution {
    public int nthUglyNumber(int n) {
        //动态规划来罗
        int[] dp=new int[n];
        dp[0]=1;
        int a=0,b=0,c=0;
        int n2;
        int n3;
        int n5;
        for(int i=1;i<n;i++){
            n2=dp[a]*2;
            n3=dp[b]*3;
            n5=dp[c]*5;
            dp[i]=Math.min(n2,Math.min(n3,n5));
            if(dp[i]==n2){
                a++;
            }
            if(dp[i]==n3){
                b++;
            }
            if(dp[i]==n5){
                c++;
            }
        }
        return dp[n-1];
    }
}

56.一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例 1:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
 

限制:

2 <= nums.length <= 10000

 

class Solution {
    public int[] singleNumbers(int[] nums) {
        //思路:对整个数组进行异或,得到的结果就是两个只出现一次的数字的抑或结果。
        //找结果中的一位为1的位,并按这个位将数组分成两个。
        //然后对两个数组进行全异或,结果为只出现过一次的数字。
        int n=nums.length;
        int res=nums[0];
        for(int i=1;i<n;i++){
            res^=nums[i];
        }
        int count=0;
        while((res&1)==0){
            res>>=1;
            count++;
        }
        int res1=0;
        int res2=0;
        for(int i=0;i<n;i++){
            if(((nums[i]>>count)&1)==1){
                res1^=nums[i];
            }else{
                res2^=nums[i];
            }
        }
        return new int[]{res1,res2};
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值