开篇

二刷剑指offer了,本来用Tyora记的笔记,发现字数到四万了就变得好卡o(╥﹏╥)o,刚好开始写博客,就转过来吧,记下来子自己看。不废话,开刷...

JZ26. 树的子结构

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

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

例如:

给定的树 A:

     3
/ \
4 5
/ \
1 2

给定的树 B:

   4
/
1

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

限制:

0 <= 节点个数 <= 10000

题解

  • 涉及树的问题,大部分用递归可以解决。
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if (A == null || B == null) return false; // 我们规定树的子结构必须是 B 树的结构和结点值完全与 A 相同
// 如果B树和A树相同, 那么也是一种子结构
// 我们可以分为三种情况判断
// 1. A树与B树完全相等
// 2. A的左子树与B树完全相等
// 3. A的右子树与B树完全相等
// 此时可以分为三个递归遍历, 因为 || 具有短路性质, 如果在某一步返回 true, 则不需要继续递归
return hasSubTree(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
public boolean hasSubTree(TreeNode A, TreeNode B) {
// 如果 B 树为空, 证明 A 树 包含 B 树
if (B == null) return true;
// 如果两棵树都不为空, 那么需要比较三样东西
// 1. A的根节点与B的根节点是否相同
// 2. A的左子树与B的左子树是否相同
// 3. A的右子树与B的右子树是否相同
if (A == null || A.val != B.val) return false;
return hasSubTree(A.left, B.left) && hasSubTree(A.right, B.right);
}
}
  • 时间复杂度 O(MN) : 其中 M,N 分别为树 A 和 树 B 的节点数量;先序遍历树 A 占用 O(M) ,每次调用 recur(A, B) 判断占用 O(N) 。
  • 空间复杂度 O(M) : 当树 A 和树 B 都退化为链表时,递归调用深度最大。当 M \leq NM≤N 时,遍历树 A 与递归判断的总递归深度为 M ;当 M>N 时,最差情况为遍历至树 A 叶子节点,此时总递归深度为 M。

二叉树的镜像

题目描述

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

输入描述:

二叉树的镜像定义:源二叉树

    8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5

递归

我们在做二叉树题目时候,第一想到的应该是用递归来解决。

仔细看下题目的输入和输出,输出的左右子树的位置跟输入正好是相反的,于是我们可以递归的交换左右子树来完成这道题。

看一下动画就明白了:

其实就是交换一下左右节点,然后再递归的交换左节点,右节点

根据动画图我们可以总结出递归的两个条件如下:

  • 终止条件:当前节点为null时返回

    交换当前节点的左右节点,再递归的交换当前节点的左节点,递归的交换当前节点的右节点

  • 时间复杂度:每个元素都必须访问一次,所以是O(n)

  • 空间复杂度:最坏的情况下,需要存放O(h)个函数调用(h是树的高度),所以是O(h)

代码实现如下:

class Solution {
public TreeNode mirrorTree(TreeNode root) {
//递归函数的终止条件,节点为空时返回
if(root==null) {
return null;
}
//下面三句是将当前节点的左右子树交换
TreeNode tmp = root.right;
root.right = root.left;
root.left = tmp;
//递归交换当前节点的 左子树
mirrorTree(root.left);
//递归交换当前节点的 右子树
mirrorTree(root.right);
//函数返回时就表示当前这个节点,以及它的左右子树
//都已经交换完了
return root;
}
}

迭代

递归实现也就是深度优先遍历的方式,那么对应的就是广度优先遍历。

广度优先遍历需要额外的数据结构--队列,来存放临时遍历到的元素。

深度优先遍历的特点是一竿子插到底,不行了再退回来继续;而广度优先遍历的特点是层层扫荡。

所以,我们需要先将根节点放入到队列中,然后不断的迭代队列中的元素。

对当前元素调换其左右子树的位置,然后:

  • 判断其左子树是否为空,不为空就放入队列中
  • 判断其右子树是否为空,不为空就放入队列中

动态图如下:

代码实现:

class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root==null) {
return null;
}
//将二叉树中的节点逐层放入队列中,再迭代处理队列中的元素
LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
while(!queue.isEmpty()) {
//每次都从队列中拿一个节点,并交换这个节点的左右子树
TreeNode tmp = queue.poll();
TreeNode left = tmp.left;
tmp.left = tmp.right;
tmp.right = left;
//如果当前节点的左子树不为空,则放入队列等待后续处理
if(tmp.left!=null) {
queue.add(tmp.left);
}
//如果当前节点的右子树不为空,则放入队列等待后续处理
if(tmp.right!=null) {
queue.add(tmp.right);
} }
//返回处理完的根节点
return root;
}
}

二叉树的层序遍历

题目描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

示例1

输入

{5,4,#,3,#,2,#,1}

返回值

[5,4,3,2,1]

方法:层次遍历

这道题就是一个模板题,对队列的使用。因为要满足先进先出的特性。

  1. 初始化:一个队列queue<TreeNode*> q, 将root节点入队列q
  2. 如果队列不空,做如下操作:
  3. 弹出队列头,保存为node,将node的左右非空孩子加入队列
  4. 做2,3步骤,知道队列为空

代码:

public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> res = new ArrayList<>();
LinkedList<TreeNode> list = new LinkedList<>();
if(root == null) return res;
list.add(root);
while(!list.isEmpty()){
TreeNode temp = list.poll();
res.add(temp.val);
if(temp.left != null) list.add(temp.left);
if(temp.right != null) list.add(temp.right);
}
return res;
}
}

二叉树的最大深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

题解:递归法

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

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

给定一棵二叉搜索树,请找出其中第k大的节点。

示例 1:

输入: root = [3,1,4,null,2], k = 1

   3
/ \
1 4
\
2

输出: 4

示例 2:

输入: root = [5,3,6,2,4,null,null,1], k = 3

       5
/ \
3 6
/ \
2 4
/
1

输出: 4

限制:1 ≤ k ≤ 二叉搜索树元素个数

题解:

搜索二叉树的中序遍历结果就是按顺序排列的。

方法一:

  • 1 中序遍历二叉树,遍历的结果放到数组list中;
  • 2 第k大的数就是 list.size() - 1;

方法二:

  • 当遍历到了第K大数的时候,就可以停止遍历了,同时,把遍历到节点对应的数保存下来即可。

代码:

//法一:
class Solution {
public int kthLargest(TreeNode root, int k) {
// clarification: root == null? k <= 1?
List<Integer> list = new ArrayList<>();
helper(root, list);
return list.get(list.size() - k);
} private void helper(TreeNode root, List<Integer> list) {
if (root == null) return;
if (root.left != null) helper(root.left, list);
list.add(root.val);
if (root.right != null) helper(root.right, list);
}
} //法二:
class Solution {
private int ans = 0, count = 0;
public int kthLargest(TreeNode root, int k) {
// clarification: root == null? k <= 1?
helper(root, k);
return ans;
} private void helper(TreeNode root, int k) {
if (root.right != null) helper(root.right, k); if (++count == k) {
ans = root.val;
return;
} if (root.left != null) helper(root.left, k);
}
}

剑指 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

限制:

0 <= 节点个数 <= 1000

class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
recur(root.left, root.right);
}
boolean recur(TreeNode L, TreeNode R) {
if(L == null && R == null) return true;
if(L == null || R == null || L.val != R.val) return false;
return recur(L.left, R.right) && recur(L.right, R.left);
}

剑指offer刷题(Tree)的更多相关文章

  1. 剑指offer刷题

    1.面试题43. 1-n整数中1出现的次数 输入一个整数 n ,求1-n这n个整数的十进制表示中1出现的次数. 例如,输入12,1-12这些整数中包含1 的数字有1.10.11和12,1一共出现了5次 ...

  2. 牛客网剑指offer刷题总结

    二维数组中的查找: 题目描述:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 两 ...

  3. LeetCode剑指Offer刷题总结(一)

    LeetCode过程中值得反思的细节 以下题号均指LeetCode剑指offer题库中的题号 本文章将每周定期更新,当内容达到10题左右时将会开下一节. 二维数组越界问题04 public stati ...

  4. 剑指offer ------ 刷题总结

    面试题3 -- 搜索二维矩阵 写出一个高效的算法来搜索 m × n矩阵中的值. 这个矩阵具有以下特性: 1. 每行中的整数从左到右是排序的. 2. 每行的第一个数大于上一行的最后一个整数. publi ...

  5. 剑指offer刷题记录

    目录 二维数组中的查找 替换空格 从尾到头打印链表 反转链表 重建二叉树 用两个栈实现队列 旋转数组的最小数字 斐波拉切数列 跳台阶 变态跳台阶 矩形覆盖 二进制中1的个数 数值的整次方 链表中倒数第 ...

  6. 剑指offer刷题笔记

    删除链表中重复的结点:较难 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4- ...

  7. 剑指offer刷题总结

    ★ 二维数组的查找 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否 ...

  8. 剑指offer刷题(算法类_2)

    排序 035-数组中的逆序对(归并排序) 题目描述 题解 代码 复杂度 029-最小的K个数(堆排序) 题目描述 题解 代码 复杂度 029-最小的K个数(快速排序) 题目描述 题解 代码 复杂度 位 ...

  9. 剑指offer刷题(算法类_1)

    斐波那契数列 007-斐波拉契数列 题目描述 题解 代码 复杂度 008-跳台阶 题目描述 题解 代码 复杂度 009-变态跳台阶 题目描述 题解 代码 复杂度 010-矩形覆盖 题目描述 题解 代码 ...

随机推荐

  1. day10 Pyhton学习

    一.昨日内容回顾 函数: 定义:对功能或者动作的封装 def 函数名(形参): 函数体 函数名(实参) return:  返回,当程序运行到return的时候,终止函数的执行 一个函数一定拥有返回值 ...

  2. go 不停模拟 写日志

    package main import ( "os" "errors" "math/rand" "time" " ...

  3. centos8平台:举例讲解redis6的ACL功能(redis6.0.1)

    一,为什么redis6要增加acl功能模块? 什么是acl? 访问控制列表(ACL)是一种基于包过滤的访问控制技术, 它可以根据设定的条件对接口上的数据包进行过滤,允许其通过或丢弃 redis6增加了 ...

  4. win10开机启动文件夹

    C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp

  5. selenium 浏览器标签切换

    from time import sleep from selenium import webdriver from selenium.webdriver.chrome.options import ...

  6. springboot入门系列(二):SpringBoot整合Swagger

    上一篇<简单搭建SpringBoot项目>讲了简单的搭建SpringBoot 项目,而 SpringBoot 和 Swagger-ui 搭配在持续交付的前后端开发中意义重大,Swagger ...

  7. Anderson《空气动力学基础》5th读书笔记 第1记——流动相似性

    在飞机真正上天之前,我们常常需要制作出缩小版的模型放在风洞中吹呀吹,尽可能地模拟真实飞行中的参数,这时我们就需要实现流动相似性,这便是本记要讲的. 文章目录 一.流动相似性的标准 二.流动相似性的应用 ...

  8. JS 计算日期相减得天数

    言简意赅不呼哨直接懂,可以封装的可以根据自己的需求封装一下 var date1="2020-10-23";var date2="2020-10-26";var ...

  9. C#8.0之后接口已经不再单纯了,我懵逼了!

    一:背景 1. 讲故事 大家在经过面向对象洗礼的时候,都了解过接口,而且知道它是一种自上而下的设计思路,举个例子,我们电脑上都有 USB 2.0 接口,蓝牙耳机实现了它可以进行充电,移动硬盘实现了它可 ...

  10. 3号随笔,搭建web环境

    创建数据库可能会遇到的问题 数据库语法错误: 如果写错了就会报错. 搭建web项目,我采用了MVC结构搭建 模型层写数据包装,controller层写业务代码,service写数据库内容,创建之后就搭 ...