开篇

二刷剑指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. 快速掌握ES6语法

    常量变量 let and const 先说说常量和变量的概念吧, 常量是说那种进行一次赋值后不会更改的值,比如说游戏账户的 ID, 变量是说赋值后有更改的需求的,比如游戏名,游戏密码. 在之前的 Ja ...

  2. python 使用pyinstaller打包程序

    使用pyinstaller 打包.py脚本,在其他计算机可以直接运行,不需要python环境 安装pyinstaller库 pip install pystaller 打包程序 pyinstaller ...

  3. Django创建表时报错django.db.utils.InternalError: (1366问题解决记录

    问题出现 执行Python manage.py makemigrations生成创建表的py文件 执行python manage.py migrate创建数据表 界面出现报错 问题原因 网上搜索原因, ...

  4. C#中的SqlBulkCopy批量插入数据

    在C#中,我们可以使用sqlBulkCopy去批量插入数据,其他批量插入方法不在讨论. 1 /// <summary> 2 /// SqlBulkCopy批量插入数据 3 /// < ...

  5. sql 删除所有存储过程

    1.执行以下sql语句即可删除所有存储过程 --/**********删除所有存储过程*************************/-- use 数据库名 go declare @tname v ...

  6. CVE-2019-15107漏洞复现

    特别说明 漏洞复现参考了teeom sec的panda潘森的文章,是根据他的思路进行复现. 搭建dockers环境, 然后安装vulhub(此处省略,自行百度) 进入vulhub/webmin/CVE ...

  7. 线程池CachedThreadPool

    没有核心线程,只有非核心线程,并且每个非核心线程空闲等待的时间为60s,采用SynchronousQueue队列 由于maximumPoolSize是无界的,所以如果线程处理任务速度小于提交任务的速度 ...

  8. day02初识判断和循环

    ​ 新的一天朝气满满,今天小东补充一些昨天没有涉及的新知识! if判断语句 if语句就像是常说的如果....那么,它会做判断,if语句有几种方式: #示例一:判断一是否等于一,等于则输出一等一,否则输 ...

  9. python获取响应某个字段值的三种方法

    近期将要对两个接口进行测试,第一个接口的响应值是第二个接口的查询条件.为了一劳永逸,打算写个自动化测试框架.因为请求和响应都是xml格式的,遇到的问题就是怎么获取xml响应的某一个值.尝试了很多博客的 ...

  10. linux系统中firewalld防火墙管理工具firewall-config(GUI图形用户界面)

    firewall-config是firewalld防火墙管理工具的GUI(图形用户界面)版本,几乎可以实现所有以命令行来执行的操作. firewall-config的界面如下图(在终端直接运行fire ...