剑指offer题目代码总结

文章目录

剑指 Offer 26. 树的子结构

二叉树匹配类问题总结

B 属于 A 的一部分也可以,没必要一直匹配到叶子节点

递归解法

在这里插入图片描述
recur的返回条件:

当节点 B 为空:说明树 B 已匹配完成(越过叶子节点),因此返回 true ;
当节点 A 为空:说明已经越过树 A 叶子节点,即匹配失败,返回 false;

剑指 Offer 28. 对称的二叉树

1.递归解法

在这里插入图片描述

2.非递归解法(队列)

在这里插入图片描述

剑指 Offer 27. 二叉树的镜像

1.递归解法

在这里插入图片描述

2.非递归解法(栈或队列)

​​在这里插入图片描述
在这里插入图片描述

剑指 Offer 32 - I. 从上到下打印二叉树

非空的不加入队列
在这里插入图片描述

非空的也加入队列,出队之后进行判断
在这里插入图片描述

剑指 Offer 32 - II. 从上到下打印二叉树 II

队列(BFS)

安安写法
在这里插入图片描述

精选答案写法 就是对null的处理不太一样
在这里插入图片描述

递归DFS实现

在这里插入图片描述

剑指 Offer 32 - III. 从上到下打印二叉树 III

队列(BFS)

linkedlist首尾添加
在这里插入图片描述
arraylist首尾添加
在这里插入图片描述

递归

在这里插入图片描述

双栈法

//Java 安安 2020.3.31
//根节点作为第0行 
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {   
        List<List<Integer>> res = new ArrayList<>();

        Stack<TreeNode> s1 = new Stack<>();  //存储奇数行
        Stack<TreeNode> s2 = new Stack<>();  //存储偶数行

        if(root != null) s2.push(root);

        while(!s1.empty() || !s2.empty()) {
            List<Integer> tmp = new ArrayList<>();
            if(!s2.empty()){
                while(!s2.empty()){
                    TreeNode p = s2.pop();
                    tmp.add(p.val);
                    if(p.left != null) s1.push(p.left);
                    if(p.right != null) s1.push(p.right);
                }
                res.add(tmp);
            }

            if(!s1.empty()){
                tmp = new ArrayList<>();
                while(!s1.empty()){
                    TreeNode p = s1.pop();
                    tmp.add(p.val);
                    if(p.right != null) s2.push(p.right);
                    if(p.left != null)  s2.push(p.left);
                }         
                res.add(tmp);
            }
        }
        return res;
    }
}

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

同步

在这里插入图片描述

不同步

两个 peek() 方法返回的都是 Integer 类型 ,它们的比较不能用 ==,因为 == 用于包装类型(它们都是对象)的比较,比较的是它们的内存地址,解决方法也很简单:(1)改用 equals() 方法,(2)至少把其中一个变量转成 int 型。
在这里插入图片描述

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

模拟矩阵路径

在这里插入图片描述

按层模拟

在这里插入图片描述

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

在这里插入图片描述

剑指 Offer 33. 二叉搜索树的后序遍历序列

递归分治

在这里插入图片描述

剑指 Offer 34. 二叉树中和为某一值的路径

在这里插入图片描述

剑指 Offer 35. 复杂链表的复制

哈希表:存放新旧节点的对应关系

在这里插入图片描述

原地复制

在这里插入图片描述

剑指 Offer 38. 字符串的排列

在这里插入图片描述

剑指 Offer 39. 数组中出现次数超过一半的数字

在这里插入图片描述

剑指 Offer 40. 最小的k个数

大根堆

在这里插入图片描述

快排思想

在这里插入图片描述


//快排思想
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(arr == null || arr.length == 0 || k <= 0) return new int[0];
        if(k > arr.length) return arr;

        quickSearch(arr, 0, arr.length-1, k);
        int[] res = new int[k];
        for(int i = 0; i < k; i++){
            res[i] = arr[i];
        }
        //System.out.println("最后:" + Arrays.toString(arr));
        return res;
    }

    public void quickSearch(int[] arr, int left, int right, int k){
        int index = quickSort(arr, left, right);
        if(index == k) return;
        else if(index < k)  quickSearch(arr, index+1, right, k);
        else quickSearch(arr, left, index-1, k);
    }

    public int quickSort(int[] arr, int left, int right){

        if(left >= right) return left;
        int i = left;
        int j = right;
        int x = arr[left];

        while(i < j){
            while(i < j && arr[j] >= x){
                j--;
            }
            arr[i] = arr[j];
            while(i < j && arr[i] <= x){
                i++;
            }
            arr[j] = arr[i];        
        }
        arr[i] = x;
        // quickSort(arr, left, i-1);
        // quickSort(arr, i+1, right);
        //说明:因为quickSearch已经进行了递归,所以qucikSort就不递归啦
        return j;
    }
}

剑指 Offer 42. 连续子数组的最大和

在这里插入图片描述

剑指 Offer 43. 1~n整数中1出现的次数

在这里插入图片描述

剑指 Offer 44. 数字序列中某一位的数字

在这里插入图片描述

剑指 Offer 45. 把数组排成最小的数

在这里插入图片描述

剑指 Offer 46. 把数字翻译成字符串

在这里插入图片描述
dp空间优化:
在这里插入图片描述

剑指 Offer 47. 礼物的最大价值

初始dp

在这里插入图片描述

空间优化,直接在原数组进行修改

在这里插入图片描述

代码简洁

在这里插入图片描述

剑指 Offer 48. 最长不含重复字符的子字符串

滑动窗口

在这里插入图片描述

动态规划

在这里插入图片描述

剑指 Offer 49. 丑数

dp

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

剑指 Offer 50. 第一个只出现一次的字符

哈希表

在这里插入图片描述
这个的代码太简洁了
在这里插入图片描述

有序哈希表

在这里插入图片描述

剑指 Offer 52. 两个链表的第一个公共节点

哈希表法

在这里插入图片描述

双指针 浪漫相遇

在这里插入图片描述

剑指 Offer 54. 二叉搜索树的第k大节点

在这里插入图片描述

剑指 Offer 55 - I. 二叉树的深度

dfs递归

在这里插入图片描述
在这里插入图片描述

BFS

在这里插入图片描述

剑指 Offer 55 - II. 平衡二叉树

递归

在这里插入图片描述

剪枝优化

在这里插入图片描述

剑指 Offer 53 - I. 在排序数组中查找数字 I

安安自己的题解 三种解法
两端都闭的写法(不存在也返回 返回的刚好是插入点的位置)

1. target+0.5 -0.5 左右都可

在这里插入图片描述

在这里插入图片描述
最后结果:7-3 = 4 所以4有4个

2. target target-1 只能是右边

在这里插入图片描述
在这里插入图片描述
最后结果:6-2 = 4 所以4有4个

3.target的左右边界

剑指 Offer 53 - II. 0~n-1中缺失的数字

类似于上题的±0.5 因此都是数组中不存在那个数值的情况
在这里插入图片描述

剑指 Offer 56 - I. 数组中数字出现的次数

排序+下标遍历 anan

//anan 2020.8.18
class Solution {
    public int[] singleNumbers(int[] nums) {
        //System.out.println(Arrays.toString(nums));
        Arrays.sort(nums);
        //System.out.println(Arrays.toString(nums));

        int[] res = new int[2];
        int length = nums.length;
        int count = 0;
        int i = 0;
        while(i < length-1){
            if(nums[i] == nums[i+1]){
                i+=2;
            }else{
                res[count++] = nums[i];
                i+=1;
            }
        }
        if(nums[length-1] != nums[length-2]){
            res[count++] = nums[length-1];
        }

        return res;
    }
}

异或

在这里插入图片描述

在这里插入图片描述

剑指 Offer 56 - II. 数组中数字出现的次数 II

在这里插入图片描述

剑指 Offer 57. 和为s的两个数字

双指针

在这里插入图片描述

二分法+双指针

//anan 2020.8.18  二分法+双指针
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int left = 0;
        int right = leftBound(nums, target);  
        /*
        利用二分法缩小范围
        nums = [2,7,11,15], target = 9             只需要搜索[2,7,11]
        nums = [10,26,30,31,47,60], target = 40    只需要搜索[10,26,30,31,47]
        */
        int[] res = new int[2];

        while(left < right){
            if(nums[left] + nums[right] < target){
                left++;
            }else if(nums[left] + nums[right] > target){
                right--;
            }else{
                res[0] = nums[left];
                res[1] = nums[right];
                break;
            }
        }

        return res;
    }

    //若是数组中有target,返回target第一次出现的位置
    //若是数组中没有target,返回target该插入的位置
    public int leftBound(int[] nums, int target){
        int left = 0;
        int right = nums.length-1;
        while(left <= right){
            int mid = left + (right-left)/2;
            if(nums[mid] < target){
                left = mid+1;
            }else if(nums[mid] > target){
                right = mid-1;
            }else if(nums[mid] == target){
                right = mid-1;
            }
        }

        return (left >= nums.length ? nums.length-1 : left);
    }
}

剑指 Offer 57 - II. 和为s的连续正数序列

双指针

在这里插入图片描述

滑动窗口

在这里插入图片描述

剑指 Offer 58 - I. 翻转单词顺序

API

双指针

在这里插入图片描述

stack+定位单词边界

//anan  用stack 定位单词边界 不用API
class Solution {
    public String reverseWords(String s) {
        int left = 0, right = s.length() - 1;
        // 去掉字符串开头的空白字符
        while (left <= right && s.charAt(left) == ' ') ++left;
        // 去掉字符串末尾的空白字符
        while (left <= right && s.charAt(right) == ' ') --right;

        int i = left;
        int j = left;
        Deque<String> stack = new LinkedList<>();
        StringBuffer sb = new StringBuffer();

        while(i <= j && j <= right){
            while(i <= right && s.charAt(i) == ' ') i++;
            j = i;
            while(j <= right && s.charAt(j) != ' ') j++;
            stack.push(s.substring(i, j));
            i = j;
        }
       
        return String.join(" ", stack);
    }
}

先反转整个字符串,再反转各个单词


class Solution {
    public StringBuilder trimSpaces(String s) {
        int left = 0, right = s.length() - 1;
        // 去掉字符串开头的空白字符
        while (left <= right && s.charAt(left) == ' ') ++left;

        // 去掉字符串末尾的空白字符
        while (left <= right && s.charAt(right) == ' ') --right;

        // 将字符串间多余的空白字符去除
        StringBuilder sb = new StringBuilder();
        while (left <= right) {
            char c = s.charAt(left);

            if (c != ' ') sb.append(c);
            else if (sb.charAt(sb.length() - 1) != ' ') sb.append(c);

            ++left;
        }
        return sb;
    }

    public void reverse(StringBuilder sb, int left, int right) {
        while (left < right) {
            char tmp = sb.charAt(left);
            sb.setCharAt(left++, sb.charAt(right));
            sb.setCharAt(right--, tmp);
        }
    }

    public void reverseEachWord(StringBuilder sb) {
        int n = sb.length();
        int start = 0, end = 0;

        while (start < n) {
            // 循环至单词的末尾
            while (end < n && sb.charAt(end) != ' ') ++end;
            // 翻转单词
            reverse(sb, start, end - 1);
            // 更新start,去找下一个单词
            start = end + 1;
            ++end;
        }
    }

    public String reverseWords(String s) {
        StringBuilder sb = trimSpaces(s);

        // 翻转字符串
        reverse(sb, 0, sb.length() - 1);

        // 翻转每个单词
        reverseEachWord(sb);

        return sb.toString();
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/reverse-words-in-a-string/solution/fan-zhuan-zi-fu-chuan-li-de-dan-ci-by-leetcode-sol/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 58 - II. 左旋转字符串

三次翻转

在这里插入图片描述

字符串拼接+求余简化代码

在这里插入图片描述

剑指 Offer 60. n个骰子的点数

dp



class Solution {
    public double[] twoSum(int n) {
        //下标从1开始
        int[][] dp = new int[n+1][n*6+1];
        for(int i = 1; i <= 6; i++){
            dp[1][i] = 1;
        }

        for(int i = 2; i <= n; i++){
            for(int j = i; j <= 6*i; j++){
                for(int cur = 1; cur <= 6; cur++){
                    if(j-cur > 0){
                        dp[i][j] += dp[i-1][j-cur];
                    }
                    
                }
            }
        }

        double all = Math.pow(6, n);
        double[] res = new double[n*6-n+1];
        for(int i = n; i <= n*6; i++){
            res[i-n] = dp[n][i]/all;
        }
        return res;
    }
}

dp空间优化

class Solution {
    public double[] twoSum(int n) {
        int[] dp = new int[n*6+1];
        for(int i = 1; i <= 6; i++){
            dp[i] = 1;
        }

        for(int i = 2; i <= n; i++){
            for(int j = 6*i; j >= i; j--){
                dp[j] = 0;
                for(int cur = 1; cur <= 6; cur++){
                    //if(j-cur > 0){  不能这样
                    i-1,即是对于只有对于少一个色子时,最小和最少为i-1,不可能再小了
                    if(j-cur >= i-1){
                        dp[j] += dp[j-cur];
                    }                   
                }
            }
        }

        double all = Math.pow(6, n);
        double[] res = new double[n*6-n+1];
        for(int i = n; i <= n*6; i++){
            res[i-n] = dp[i]/all;
        }
        return res;
    }
}

剑指 Offer 61. 扑克牌中的顺子

一般思路

class Solution {
    public boolean isStraight(int[] nums) {
        Arrays.sort(nums);
        int count0 = 0;
        int need0 = 0;
        for(int i = 0;i<nums.length-1;i++){
            if(nums[i]==0){
                count0++;
                continue;
            }
            if(nums[i+1]==nums[i]){
                return false;
            }
            if(nums[i+1]-nums[i]>=1){
                need0 = need0+(nums[i+1]-nums[i]-1);
            }
        }
        return need0<=count0;
    }
}

巧妙思路

在这里插入图片描述
在这里插入图片描述

剑指 Offer 65. 不用加减乘除做加法

//安安  写两个变量好理解
class Solution {
    public int add(int a, int b) {
        while(b != 0){
            int jinwei = (a & b) << 1;
            int benwei = a ^ b ;
            b = jinwei;
            a = benwei;
        }        
        return a;
    }
}
class Solution {
    public int add(int a, int b) {
        while(b != 0) { // 当进位为 0 时跳出
            int c = (a & b) << 1;  // c = 进位
            a ^= b; // a = 非进位和
            b = c; // b = 进位
        }
        return a;
    }
}

作者:jyd
链接:https://leetcode-cn.com/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/solution/mian-shi-ti-65-bu-yong-jia-jian-cheng-chu-zuo-ji-7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 66 构建乘积数组

暴力法(超时)

class Solution {
    public int[] constructArr(int[] a) {
        int n = a.length;
        int[] b = new int[n];
        for(int i = 0; i < n; i++){
            int num = 1;
            for(int j = 0; j < n; j++){
                if(j != i){
                    num *= a[j];
                }
            }
            b[i] = num;
        }
        return b;
    }
}

两个dp

在这里插入图片描述

//本质就是两个dp数组,分别维护 i 左侧、右侧的乘积和
class Solution {
    public int[] constructArr(int[] a) {

        if(a == null || a.length == 0) return new int[0];

        int n = a.length;
        int[] b = new int[n];
        int[] left = new int[n];
        int[] right = new int[n];
        left[0] = 1;
        right[n-1] = 1;

        for(int i = 1; i < n; i++){
            left[i] = left[i-1]*a[i-1];
        }

        for(int i = n-2; i >= 0; i--){
            right[i] = right[i+1]*a[i+1];
        }

        for(int i = 0; i < n; i++){
            b[i] = left[i]*right[i];
        }

        return b;
         
    }
}

剑指 Offer 67. 把字符串转换成整数

正则

原题解

//需要导入包
import java.util.regex.*;
class Solution {
    public int myAtoi(String str) {
        //清空字符串开头和末尾空格(这是trim方法功能,事实上我们只需清空开头空格)
        str = str.trim();
        //java正则表达式
        Pattern p = Pattern.compile("^[\\+\\-]?\\d+");
        Matcher m = p.matcher(str);
        int value = 0;
        //判断是否能匹配
        if (m.find()){
            //字符串转整数,溢出
            try {
                value = Integer.parseInt(str.substring(m.start(), m.end()));
            } catch (Exception e){
                //由于有的字符串"42"没有正号,所以我们判断'-'
                value = str.charAt(0) == '-' ? Integer.MIN_VALUE: Integer.MAX_VALUE;
            }
        }
        return value;
    }
}

自己根据题解稍微改了一下

/*
? 零次或一次匹配前面的字符或子表达式
+ 一次或多次匹配前面的字符或子表达式
*/
import java.util.regex.*;
class Solution {
    public int myAtoi(String s) {
        s = s.trim();

        String regex = "^[+-]?\\d+";
        Pattern r = Pattern.compile(regex);       
        Matcher m = r.matcher(s);
        int value = 0;

        if(m.find( )) {
            //System.out.println("Found value: " + m.group());
            try{
                value = Integer.parseInt(m.group());
            }catch(Exception e){
                value = s.charAt(0) == '-' ? Integer.MIN_VALUE: Integer.MAX_VALUE;
            }
        }
       
        return value;
    }
}

直接遍历判断

public class Solution {
    public int myAtoi(String str) {
        char[] chars = str.toCharArray();
        int n = chars.length;
        int idx = 0;
        while (idx < n && chars[idx] == ' ') {
            // 去掉前导空格
            idx++;
        }
        if (idx == n) {
            //去掉前导空格以后到了末尾了
            return 0;
        }
        boolean negative = false;
        if (chars[idx] == '-') {
            //遇到负号
            negative = true;
            idx++;
        } else if (chars[idx] == '+') {
            // 遇到正号
            idx++;
        } else if (!Character.isDigit(chars[idx])) {
            // 其他符号
            return 0;
        }
        int ans = 0;
        while (idx < n && Character.isDigit(chars[idx])) {
            int digit = chars[idx] - '0';
            if (ans > (Integer.MAX_VALUE - digit) / 10) {
                // 本来应该是 ans * 10 + digit > Integer.MAX_VALUE
                // 但是 *10 和 + digit 都有可能越界,所有都移动到右边去就可以了。
                return negative? Integer.MIN_VALUE : Integer.MAX_VALUE;
            }
            ans = ans * 10 + digit;
            idx++;
        }
        return negative? -ans : ans;
    }
}

作者:sweetiee
链接:https://leetcode-cn.com/problems/string-to-integer-atoi/solution/java-zi-fu-chuan-zhuan-zheng-shu-hao-dong-by-sweet/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 68 - I 二叉搜索树的最近公共祖先

迭代

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        while(root != null) {
            if(root.val < p.val && root.val < q.val) // p,q 都在 root 的右子树中
                root = root.right; // 遍历至右子节点
            else if(root.val > p.val && root.val > q.val) // p,q 都在 root 的左子树中
                root = root.left; // 遍历至左子节点
            else break;
        }
        return root;
    }
}


迭代2

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p.val > q.val) { // 保证 p.val < q.val
            TreeNode tmp = p;
            p = q;
            q = tmp;
        }
        while(root != null) {
            if(root.val < p.val) // p,q 都在 root 的右子树中
                root = root.right; // 遍历至右子节点
            else if(root.val > q.val) // p,q 都在 root 的左子树中
                root = root.left; // 遍历至左子节点
            else break;
        }
        return root;
    }
}

递归

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root.val < p.val && root.val < q.val)
            return lowestCommonAncestor(root.right, p, q);
        if(root.val > p.val && root.val > q.val)
            return lowestCommonAncestor(root.left, p, q);
        return root;
    }
}

剑指 Offer 68 - II 二叉树的最近公共祖先

递归

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(left == null) return right;
        if(right == null) return left;
        return root;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安安csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值