《剑指 Offer I》刷题笔记 41_50

  • 排序(中等)
    • 41. 最小的k个数#
      • _解法1:排序 API + 数组复制 API
    • 42. 数据流中的中位数
      • _解法1:暴力
  • 搜索和回溯算法(中等)
    • 43. 二叉树的深度
      • _解法1:递归 DFS
      • _解法2:BFS
    • 44. 平衡二叉树
      • _解法1:DFS + 判断深度
      • 解法2:后序遍历 + 剪枝
    • 45. 求 1 + 2 + ... + n
      • _解法1:数学函数 + 求和公式
      • 解法2:短路效应实现递归
    • 46. 二叉搜索树的最近公共祖先
      • _解法1:DFS + 回溯 + 剪枝
      • 解法2:递归 / 迭代(相同思路)
    • 47. 二叉树的最近公共祖先
      • _解法1:暴力
      • 解法2:递归
  • 分治算法(中等)
    • 48. 重建二叉树**
      • 解法1:递归
    • 49. 数值的整数次方*
      • 解法1:快速幂(递归)
      • 解法2:快速幂(迭代)
    • 50. 二叉搜索树的后序遍历序列**
      • 解法1:递归
      • 解法2:单调栈(待续)

小标题以 _ 开头的题目和解法代表独立想到思路及编码完成,其他是对题解的学习。

VsCode 搭建的 Java 环境中 sourcePath 配置比较麻烦,可以用 java main.java 运行(JDK 11 以后)

Go 的数据结构:LeetCode 支持 https://godoc.org/github.com/emirpasic/gods 第三方库。

go get github.com/emirpasic/gods

排序(中等)

41. 最小的k个数#

题目:剑指 Offer 40. 最小的k个数

目前对排序的题目先用 API 做,手撕排序算法后面再说…

_解法1:排序 API + 数组复制 API

class Solution {public int[] getLeastNumbers(int[] arr, int k) {Arrays.sort(arr);return Arrays.copyOf(arr, k);}
}

42. 数据流中的中位数

题目:剑指 Offer 41. 数据流中的中位数

_解法1:暴力

思路:使用 List 存储数据

class MedianFinder {List<Integer> list;public MedianFinder() {list = new ArrayList<>();}public void addNum(int num) {list.add(num);}public double findMedian() {Collections.sort(list);if ((list.size() & 1) == 1) // 奇数return list.get(list.size() / 2);else { // 偶数int size = list.size();return (list.get(size / 2 - 1) + list.get(size / 2)) / 2.0;}}
}

思路:使用 int[] 数组存储数据

class MedianFinder {int[] arr;int size = 0;int medium = 0;public MedianFinder() {arr = new int[50000];}public void addNum(int num) {arr[size++] = num;}public double findMedian() {Arrays.sort(arr, 0, size);if ((size & 1) == 0)return (arr[(size >> 1) - 1] + arr[size >> 1]) / 2.0;// 偶elsereturn arr[size >> 1]; // 奇}
}

搜索和回溯算法(中等)

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

43. 二叉树的深度

_解法1:递归 DFS

思路:现在这种题目的递归都是很简单的了。

class Solution {public int maxDepth(TreeNode root) {if (root == null) return 0;return Math.max(maxDepth(root.left) , maxDepth(root.right)) + 1;}
}

_解法2:BFS

思路:BFS 利用队列遍历,这个思路也已经很熟悉了。

class Solution {public int maxDepth1(TreeNode root) {if (root == null) return 0;Queue<TreeNode> queue = new LinkedList<>() {{ offer(root); }};int res = 0;while (!queue.isEmpty()) {res++;for (int i = queue.size() - 1; i >= 0; i--) {TreeNode node = queue.poll();if (node.left != null)queue.offer(node.left);if (node.right != null)queue.offer(node.right);}}return res;}
}

44. 平衡二叉树

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

_解法1:DFS + 判断深度

思路:这题配合上一题 剑指 Offer 55 - I. 二叉树的深度 还是比较容易做出来的

class Solution {public boolean isBalanced(TreeNode root) {if (root == null) return true;// 左右子树深度差 > 1 则不平衡if (Math.abs(maxDepth(root.left) - maxDepth(root.right)) > 1)return false;return isBalanced(root.left) && isBalanced(root.right);}/*** 获取二叉树的深度*/int maxDepth(TreeNode node) {if (node == null) return 0;return Math.max(maxDepth(node.left), maxDepth(node.right)) + 1;}
}

解法2:后序遍历 + 剪枝

题解:面试题55 - II. 平衡二叉树(从底至顶、从顶至底,清晰图解)

class Solution {public boolean isBalanced(TreeNode root) {if (root == null) return true;return dfs(root) != -1;}/*** 计算树的深度 + 剪枝* 当有左右子树高度差 >= 2 的情况* 直接返回 -1 表示不平衡*/int dfs(TreeNode node) {if (node == null) return 0;int left = dfs(node.left);if (left == -1) return -1; // 剪枝int right = dfs(node.right);if (right == -1) return -1; // 剪枝return Math.abs(left - right) < 2 ? Math.max(left, right) + 1 : -1;}}

45. 求 1 + 2 + … + n

题目:剑指 Offer 64. 求1+2+…+n

_解法1:数学函数 + 求和公式

class Solution {public int sumNums(int n) {return (int) (Math.pow(n, 2) + n) >> 1;}
}

解法2:短路效应实现递归

题解:面试题64. 求 1 + 2 + … + n(逻辑符短路,清晰图解)

常规的递归做法:

class Solution {public int sumNums(int n) {if (n == 1) return 1;return sumNums(n - 1) + n;}
}

其实递归出口就是 n == 1 的时候,通过 && 的短路效应实现递归出口:

class Solution {public int sumNums(int n) {boolean x = n > 1 && (n += sumNums(n - 1)) > 0;return n;}
}

同样的思路,不同的写法:

class Solution {int res = 0; // 利用类变量计算值public int sumNums(int n) {boolean x = n > 1 && sumNums(n - 1) > 0;res += n;return res;}
}

46. 二叉搜索树的最近公共祖先

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

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉搜索树中。

_解法1:DFS + 回溯 + 剪枝

解法2:递归 / 迭代(相同思路)

题解:面试题68 - I. 二叉搜索树的最近公共祖先(迭代 / 递归,清晰图解)

这题由于二叉搜索树的特性,递归 和 迭代 实现的思路是一样的:

class Solution {/*** 递归*/public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {// 递归出口,此题可以不判断,因为题目保证有解// p, q 都 < root, 说明公共祖先一定在 root 左边,往左找if (p.val < root.val && q.val < root.val)return lowestCommonAncestor(root.left, p, q);// p, q 都 > root, 说明公共祖先一定在 root 右边,往右找if (p.val > root.val && q.val > root.val)return lowestCommonAncestor(root.right, p, q);// p, q 分别在 root 的左右,则为公共祖先return root;}
}
class Solution {/*** 迭代 */public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {while (root != null) {if (p.val < root.val && q.val < root.val)root = root.left;else if (p.val > root.val && q.val > root.val)root = root.right;else break;}return root;}
}

47. 二叉树的最近公共祖先

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

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉树中。

_解法1:暴力

class Solution {// 存储找到 p 为止走过的路径List<TreeNode> pList = new ArrayList<>();// 存储找到 q 为止走过的路径List<TreeNode> qList = new ArrayList<>();// 用来判断 pList, qList 是否继续存储路径boolean pFlag = false, qFlag = false; public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {dfs(root, p, q);// pList 从后往前找,直到找到 qList 中也包含的节点,即为 公共祖先for (int i = pList.size() - 1; i >= 0; i--) if (qList.contains(pList.get(i))) return pList.get(i);return null;}/*** 深度优先搜索 root, 同时将走过的路径分别加入 pList、qList* 直到 p、q 节点都已经遍历过*/void dfs(TreeNode root, TreeNode p, TreeNode q) {if (root == null) return;if (!pFlag) pList.add(root);if (!qFlag) qList.add(root);// 找到某个节点后,以后就不再记录它的路径if (root.val == p.val) pFlag = true;if (root.val == q.val) qFlag = true;// 剪枝:都找到就结束递归if (pFlag && qFlag) return; // 剪枝dfs(root.left, p, q);dfs(root.right, p, q);// 回溯(只有没找到的节点需要回溯)if (!pFlag) pList.remove(pList.size() - 1);if (!qFlag) qList.remove(qList.size() - 1);}
}

解法2:递归

题解:剑指 Offer 68 - II. 二叉树的最近公共祖先(DFS ,清晰图解)

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if (root == null) return null;// 如果 p, q 中有等于 root 的,那么它们的最近公共祖先为该节点if (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;// 左右都找不到, 则当前 root 为最近公共祖先return root;}
}

分治算法(中等)

48. 重建二叉树**

题目:剑指 Offer 07. 重建二叉树

解法1:递归

例子是别的地方借的,题解思路是我写的:

对于以下数据:
preorder = [3,9,6,10,20,15,7] # 先序遍历
inorder = [6,9,10,3,15,20,7] # 中序遍历1、根据[先序遍历],可以确定【3】为根节点,然后根据【3】在[中序遍历]进行划分:
- [6, 9, 10] 是【左子树】
- [3] 是此轮的【根节点】
- [15, 20, 7] 是【右子树】2、对【左子树】进行同样的操作,递归传入的数组:
preorder = [9, 6, 10] # 先序遍历
inorder = [6, 9, 10] # 中序遍历3、对【右子树】进行同样的操作,递归传入的数组:
preorder = [20, 15, 7] # 先序遍历
inorder = [15, 20, 7] # 中序遍历
方法步骤归纳:
1. 由【先序遍历第一个数字】得出根节点
2. 由【中序遍历结合根节点的值得出】:哪些数字是左子树,哪些数字是右子树,并划分出来
3. 重复 1-2 步

代码:

class Solution {public TreeNode buildTree(int[] preorder, int[] inorder) {if (preorder.length == 0) return null;// [先序遍历]可以直接知道:根节点的值 rootValint rootVal = preorder[0]; // 3// 在[中序遍历]中寻找 rootVal 的索引int rootIndex = 0;for (int i = 0; i < inorder.length; i++) {if (inorder[i] == rootVal) {rootIndex = i; // 3break;}}// 构造根节点TreeNode root = new TreeNode(rootVal);// 递归root.left = buildTree(// [9, 6, 10]Arrays.copyOfRange(preorder, 1, rootIndex + 1),// [6, 9, 10]Arrays.copyOfRange(inorder, 0, rootIndex));root.right = buildTree(// [20, 15, 7]Arrays.copyOfRange(preorder, rootIndex + 1, preorder.length),// [15, 20, 7]Arrays.copyOfRange(inorder, rootIndex + 1, inorder.length));return root;}
}

49. 数值的整数次方*

题目:剑指 Offer 16. 数值的整数次方

解法1:快速幂(递归)

class Solution {public double myPow(double x, int n) {// 递归出口if (n == 0) return 1;if (n == 1) return x;if (n == -1) return 1 / x;// 计算出 x^(n/2)// 则 x^n = x^(n/2) * x^(n/2)double half = myPow(x, n >> 1);half *= half;// 如果指数是奇数,由于是 2 倍的缩小,会遗漏一次乘 xreturn (n & 1) == 1 ? half * x : half;}
}

解法2:快速幂(迭代)

正常写法:

class Solution {public double myPow(double x, int n) {// 处理 n < 0 情况if (n < 0) {x = 1 / x;n = -n;}double res = 1.0;while (n != 0) {// 最后一位为1,需要乘上该位的权重if ((n & 1) == 1)res*= x;x *= x;n >>= 1;}return res;}
}

但是以上这种写法无法通过所有测试用例…因为存在以下这种测试用例:

1.00000
-2147483648

因此为了通过这些 n 极其大的用例,需要用 long 类型的变量来接收一下:

class Solution {public double myPow(double x, int n) {long b = n;// 处理 n < 0 情况if (b < 0) {x = 1 / x;b = -b;}double res = 1.0;while (b != 0) {// 最后一位为1,需要乘上该位的权重if ((b & 1) == 1)res*= x;x *= x;b >>= 1;}return res;}
}

50. 二叉搜索树的后序遍历序列**

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

解法1:递归

题解:递归和栈两种方式解决,最好的击败了100%的用户

class Solution {public boolean verifyPostorder(int[] postorder) {// 特殊情况:只有1个节点if (postorder.length == 1) return true;return verify(postorder, 0, postorder.length - 1);}/*** 判断后序遍历数组中的指定区间, 是不是某二叉搜索树的后序遍历结果* @param postorder 后序遍历数组* @param left 开始索引* @param right 结束索引* @return 是不是某二叉搜索树的后序遍历结果*/boolean verify(int[] postorder, int left, int right) {// 区间不合法直接返回 trueif (left >= right) return true;// 当前树的根节点(后序遍历中最后一个值)int rootValue = postorder[right];// 从当前区域找到第一个 > 根节点的,说明后续区域数值都在右子树中int k = left;while (k < right && postorder[k] < rootValue) k++;// 判断后续的区域是否所有值都 > 当前根节点,出现 < 的值就直接返回 falsefor (int i = k; i < right; i++)if (postorder[i] < rootValue) return false;// 对左右子节点进行递归调用return verify(postorder, left, k -1) && verify(postorder, k, right - 1);}
}

解法2:单调栈(待续)

题解:递归和栈两种方式解决,最好的击败了100%的用户

《剑指 Offer I》刷题笔记 41 ~ 50 题相关推荐

  1. 《剑指offer》刷题笔记(发散思维能力):求1+2+3+...+n

    <剑指offer>刷题笔记(发散思维能力):求1+2+3+-+n 转载请注明作者和出处:http://blog.csdn.net/u011475210 代码地址:https://githu ...

  2. 《剑指offer》刷题——【链表】从尾到头打印链表

    <剑指offer>刷题--[链表]-<从尾到头打印链表> 问题分析: 递归实现: 1. 无返回值 2. 有返回值(ArrayList) 问题分析: 从头到尾打印链表比较简单,那 ...

  3. 《剑指Offer》刷题之最小的K个数

    <剑指Offer>刷题之最小的K个数 我不知道将去向何方,但我已在路上! 时光匆匆,虽未曾谋面,却相遇于斯,实在是莫大的缘分,感谢您的到访 ! 题目: 给定一个数组,找出其中最小的K个数. ...

  4. 《剑指offer》刷题总结

    从三月初开始刷剑指offer上面的题,到现在花了近二十天的时间终于刷完了.应该说,掌握上面的技巧应付一些公司面试题和小公司的笔试题是完全没有问题的.之前参加一个公司笔试,算法题就有一题是剑指offer ...

  5. 【剑指Offer】个人学习笔记_41_数据流中的中位数

    目录 题目: [剑指 Offer 41. 数据流中的中位数](https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lc ...

  6. 【剑指Offer】个人学习笔记_15_二进制中1的个数

    目录 题目: [剑指 Offer 15. 二进制中1的个数](https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/) 题 ...

  7. 【剑指Offer】个人学习笔记_61_扑克牌中的顺子

    目录 题目: [剑指 Offer 61. 扑克牌中的顺子](https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof/) 题目分 ...

  8. 【剑指Offer】个人学习笔记_46_把数字翻译成字符串

    目录 题目: [剑指 Offer 46. 把数字翻译成字符串](https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan- ...

  9. 【剑指Offer】个人学习笔记_38_字符串的排列

    目录 题目: [剑指 Offer 38. 字符串的排列](https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/) 题目分析 初始解 ...

最新文章

  1. python 网关控制家居_在树莓派上搭建智能家居网关
  2. 基于VC++的GDI常用坐标系统及应用
  3. spring的aop配置-配置将通知织入目标对象
  4. springboot 拦截器的坑 WebMvcConfigurationSupport 失效
  5. 换个思路理解Javascript中的this
  6. 【转】Boost库概述
  7. 网际风全推数据接口_网际风数据接口飞狐交易师版简要说明.doc
  8. matlab opnet,OPNET中设计网络 MATLAB
  9. 2021年N1叉车司机考试总结及N1叉车司机操作证考试
  10. Yii框架里的一些zii用法
  11. 查看路由器地址是否是公网ip
  12. 日志组的状态:CURRENT、ACTIVE、INACTIVE
  13. Postgresql默认用户名与密码
  14. 【精品盘点】2020年最受欢迎的6个知识库整理软件!
  15. Ubuntu18.04解决蓝牙耳机无法连接问题
  16. 第十周博文作业,项目2 (1)(2)
  17. 笔趣阁小说站的爬虫小程序
  18. 达特茅斯学院计算机phd,2020年达特茅斯学院博士申请时间
  19. 尘埃落定性描写细节摘要_解释“细节”和“摘要”元素
  20. “注定不凡“汇众教育17周年 V10.0发布会上海站“剧透”抢先看

热门文章

  1. 我弟弟用管理员身份把我和爸妈的WIFI设置了,现在就他能用网,气不过,有办法让我出口气吗?
  2. 买断式软件逐渐向订阅式软件发展,是不是资本想一直割韭菜?
  3. Dubbo-Admin 2.7 本地安装和部署
  4. LeetCode-删除中间节点
  5. 增强的PolyBase SQL 2019-外部表SQL Server,目录视图和下推式
  6. ssis 创建ssisdb_使用SSIS创建备份
  7. sql limit 子句_SQL Server TOP子句概述和示例
  8. Git部署远程仓库至github
  9. 【BZOJ4710】[JSOI2011]分特产(容斥)
  10. python入门(七):CGI编程