二叉树(2)
101. 对称二叉树 - 力扣(LeetCode) (leetcode-cn.com)
给你一个二叉树的根节点 root
, 检查它是否轴对称。
递归法:
1.首先确定递归的参数是两个子树节点,返回值是bool类型。
2.确定中止条件
左节点为空,右节点不为空,不对称,return false
左不为空,右为空,不对称 return false
左右都为空,对称,return true
左右都不为空,比较节点数值,不相同就return false
3.确定单层递归的逻辑,即比较子树外侧节点和内侧节点,如果有一个不一样就返回false,否则返回true
1 | /** |
迭代法:
用队列或者栈处理节点,判断的逻辑和递归相同。
1 | /** |
100. 相同的树 - 力扣(LeetCode) (leetcode-cn.com)
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
1 | //递归法 |
1 | //迭代法 |
递归方法也是深度优先搜索,迭代法也是广度优先搜索。
572. 另一棵树的子树 - 力扣(LeetCode) (leetcode-cn.com)
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。
1 | /** |
思路:递归法
- Compare函数用于递归检查需要判断的树对应结点是否相同;(内层递归检查函数)
- isSubtree函数则用于判断是否为子树;(外层递归判断函数)
104. 二叉树的最大深度 - 力扣(LeetCode) (leetcode-cn.com)
递归法:后序遍历
1 | /** |
迭代法:层序遍历
1 | /** |
559. N 叉树的最大深度 - 力扣(LeetCode) (leetcode-cn.com)
相同的思路,就是左右孩子换成了多个孩子节点。
递归法:
1 | /* |
迭代法:
1 | /* |
111. 二叉树的最小深度 - 力扣(LeetCode) (leetcode-cn.com)
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
递归法:
1 | /** |
这里特别要注意题里要求最小深度必须世道叶子节点,因此和求最大深度的递归逻辑有一点区别。
迭代法:
1 | /** |
注意迭代法是当节点左右孩子都为空时为叶子节点,此时返回树的深度。
222. 完全二叉树的节点个数 - 力扣(LeetCode) (leetcode-cn.com)
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h - 1) 个节点。
思路
1 | //方法1:按普通的二叉树处理,层序遍历(迭代法),时间复杂度是O(n),空间复杂度也是O(n) |
1 | //递归法,时间复杂度是O(n),空间复杂度也是O(logn) |
1 | //针对完全二叉树的特殊解法,递归法,时间复杂度:$O(\log n × \log n)$空间复杂度:$O(\log n)$ |
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数,理论上用先序遍历。
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数,理论上用后序遍历,后序遍历也就是递归。
110. 平衡二叉树 - 力扣(LeetCode) (leetcode-cn.com)
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
1 | //递归法 |
1 | //迭代法,后序遍历求高度,了解 |
如果是模拟前中后序遍历就用栈,如果是适合层序遍历就用队列,当然还是其他情况,那么就是 先用队列试试行不行,不行就用栈。
257. 二叉树的所有路径 - 力扣(LeetCode) (leetcode-cn.com)
给你一个二叉树的根节点 root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
本题应该用先序遍历,而且涉及到了回溯。
1 | /** |
path.pop_back();
体现回溯的思想
1 | class Solution { |
如上代码精简了不少,也隐藏了不少东西。
注意在函数定义的时候void traversal(TreeNode* cur, string path, vector<string>& result)
,定义的是string path
,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果。如果是引用相当于修改的是同一个对象,这样就把原来的path改变了。
回溯隐藏在traversal(cur->left, path + "->", result);
中的 path + "->"
。 每次函数调用完,path依然是没有加上”->” 的,这就是回溯了。
1 | /** |
迭代法,就是一个前序遍历,比较好理解。