刷题笔记| 快速刷完67道剑指offer(Java版)

Posted 刹那芳间-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了刷题笔记| 快速刷完67道剑指offer(Java版)相关的知识,希望对你有一定的参考价值。

本文已收录于专栏
🌻
《刷题笔记》

文章目录

前言

题目来源参考阿秀学长的刷题笔记,小戴只是把 C++的题解改成了 Java版本,并整理了其他思路,便于自己的学习~

如果解题有更好的方法,本文也会及时进行更新~

希望对你有帮助~ 一起加油哇~

🎨 1、合并两个有序链表

牛客原题链接

题目描述

输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的

思路一(递归)

/*
public class ListNode 
    int val;
    ListNode next = null;

    ListNode(int val) 
        this.val = val;
    
*/
public class Solution 
    public ListNode Merge(ListNode list1,ListNode list2) 
        if(list1 == null)
            return list2;
        
        if(list2 == null)
            return list1;
        
        if(list1.val < list2.val)
            list1.next = Merge(list1.next,list2);
            return list1;
        else
            list2.next = Merge(list1,list2.next);
            return list2;
        
        
    

思路二(双指针)

/*
public class ListNode 
    int val;
    ListNode next = null;

    ListNode(int val) 
        this.val = val;
    
*/
public class Solution 
    public ListNode Merge(ListNode list1,ListNode list2) 
        if(list1 == null)
            return list2;
        
        if(list2 == null)
            return list1;
        
        // 创建一个头结点
        ListNode head = new ListNode(0);
        ListNode cur = head;

        while(list1!=null && list2!=null)
            if(list1.val <= list2.val)
                cur.next = list1;
                list1 = list1.next;
            else
                cur.next = list2;
                list2 = list2.next;
            
            cur = cur.next;
        

        if(list1 != null)
            cur.next = list1;
        else
            cur.next = list2;
        

        return head.next;
    

🎨 2、树的子结构

牛客原题链接

题目描述

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

思路一(递归)

/**
public class TreeNode 
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) 
        this.val = val;

    


*/
public class Solution 
    public boolean HasSubtree(TreeNode root1, TreeNode root2) 

        if (root1 == null || root2 == null) 
            return false;
        

        return HasSub(root1, root2) || HasSubtree(root1.left, root2) ||
               HasSubtree(root1.right, root2);
    

    public boolean HasSub(TreeNode root1, TreeNode root2) 
        // 不匹配的情况有很多,先找匹配的情况
        // 遍历完root2,root2为空时,说明root2的所有结点都与root1的子结构匹配上
        if (root2 == null) 
            return true;
        

        // 不匹配的情况
        // root2不为null的时候,root1为null了,显然不匹配
        if(root1 == null)
            return false;
         
        // root1和root2都不为空,但是数值不同,所以不匹配
        if(root1.val != root2.val)
            return false;
        

        // 到这里说明这个点是匹配的,继续判断左子树和右子树是否匹配
        return HasSub(root1.left,root2.left) && HasSub(root1.right,root2.right);
    

🎨 3、二叉树的镜像

牛客原题链接

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像

思路一(递归)

先把根节点的左右子树进行交换,再递归子树进行镜像,不断交换底下的左右节点

import java.util.*;

/*
 * public class TreeNode 
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) 
 *     this.val = val;
 *   
 * 
 */

public class Solution 
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pRoot TreeNode类 
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) 
        // write code here
        // 空树返回
        if(pRoot == null) return null;
        // 交换
        TreeNode temp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = temp;
        // 递归
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    

🎨 4、顺时针打印矩阵

牛客原题链接

题目描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:

[[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]

则依次打印出数字

[1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]

思路一(边界模拟法)

import java.util.ArrayList;
public class Solution 
    public ArrayList<Integer> printMatrix(int [][] matrix) 
       ArrayList<Integer> res = new ArrayList<>();
       if(matrix.length == 0)
        return res;
       
       // 左边界
       int left = 0;
       // 右边界
       int right = matrix[0].length - 1;
       // 上边界
       int up = 0;
       // 下边界
       int down = matrix.length - 1;
       while(left <= right && up <= down)
            // 上边界的从左到右
            for(int i = left; i <=right; i++)
                res.add(matrix[up][i]);
            
            up++;
            if(up > down)
                break;
            
            // 右边界的从上到下
            for(int i = up; i <= down; i++)
                res.add(matrix[i][right]);
            
            right--;
            if(left > right)
                break;
            
            // 下边界的从右到左
            for(int i = right; i >= left; i--)
                res.add(matrix[down][i]);
            
            down--;
            if(up > down)
                break;
            
            // 左边界的从下到上
            for(int i = down; i >= up; i--)
                res.add(matrix[i][left]);
            
            left++;
            if(left > right)
                break;
            
       
       return res;
    

剑指Offer018 - 020 刷题笔记 回文 双指针DP中心扩展


018 - 剑指 Offer II 018. 有效的回文【双指针】

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
    public boolean isPalindrome(String s) {
        int left = 0, right = s.length() - 1;
        while (left < right) {
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                left++;
            }
            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                right--;
            }
            if (left < right) {
                if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                    return false;
                }
                left++;
                right--;
            }
        }
        return true;
    }
}

019 - 剑指 Offer II 019. 最多删除一个字符得到回文【贪心+双指针】

设两个指针从字符串两端向中心靠拢,如果左右指针指向字符相等,则继续向中心靠拢;如果不相等,则必须删除其中一个,那么就产生了两种情况:删除左边字符、或 删除右边字符,只要以上这两种情况有一个符合是回文串就返回 t r u e true true,否则返回 f a l s e false false

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
    public boolean validPalindrome(String s) {
        int left = 0, right = s.length() - 1;
        while (left < right) {
            if (s.charAt(left) == s.charAt(right)) {
                left++;
                right--;
            } else {
                return isPalindrome(s, left + 1, right) || isPalindrome(s, left, right - 1); // 删除左 || 删除右
            }
        }

        return true;
    }
	
    // 判断字符串s是否是回文串
    private boolean isPalindrome(String s, int left, int right) {
        while (left < right) {
            if (s.charAt(left) == s.charAt(right)) {
                left++;
                right--;
            } else {
                return false;
            }
        }

        return true;
    }
}

020 - 剑指 Offer II 020. 回文子字符串的个数【动态规划、中心扩展】

方法一:动态规划

设置 d p [ i ] [ j ] dp[i][j] dp[i][j]表示字符串 s s s [ i , j ] [i,j] [i,j]区间的子串是否是一个回文串。

状态转移方程:$dp[i][j] = \\left{
\\begin{aligned}
true, 如果 s[i] == s[j] 并且 (j-i<=1 || dp[i+1][j-1]) \\
fasle, 其他
\\end{aligned}
\\right.
$

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n 2 ) O(n^2) O(n2)
class Solution {
    // 动态规划
    public int countSubstrings(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];
        int cnt = 0;

        // 按列计算dp
        for (int j = 0; j < n; j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1])) {
                    dp[i][j] = true;
                    cnt++;
                }
            }
        }

        return cnt;
    }
}

方法二:中心扩展

对于一个子串,选择最中间的a作为中心点,往两边扩展,如果中心串是回文串,并且向两侧扩展的左右两个字符一样,则产生了一个新的回文串,依次继续扩展。

对于“中心点”,可以是一个字符,也可能是两个字符,这样中心点的个数就有 2 ∗ n − 1 2*n-1 2n1个,其中单个字符的中心点有 n n n个,两个字符的中心点有 n − 1 n-1 n1个。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
    // 中心扩展
    public int countSubstrings(String s) {
        int n = s.length();
        int freq = 2 * n - 1;
        int cnt = 0;

        for (int i = 0; i < freq; i++) {
            int left = i / 2, right = i / 2 + i % 2;
            while (left >= 0 && right < n && s.charAt(left) == s.charAt(right)) {
                left--;
                right++;
                cnt++;
            }
        }

        return cnt;
    }
}

以上是关于刷题笔记| 快速刷完67道剑指offer(Java版)的主要内容,如果未能解决你的问题,请参考以下文章

力扣(LeetCode)剑指offer刷题笔记(java),已完结!!!

力扣(LeetCode)剑指offer刷题笔记(java),已完结!!!

LeetCode Java刷题笔记—剑指 Offer 22. 链表中倒数第k个节点

LeetCode Java刷题笔记—剑指 Offer 54. 二叉搜索树的第k大节点

剑指Offer018 - 020 刷题笔记 回文 双指针DP中心扩展

java刷题--剑指offer05 替换空格