-
Notifications
You must be signed in to change notification settings - Fork 116
二叉树相关算法
- 144. 二叉树前序遍历
- 94. 二叉树的中序遍历
- 145. 二叉树的后序遍历
- 102. 二叉树的层序遍历
- 100. 相同的树
- 101. 对称二叉树
- 104. 二叉树的最大深度
- 110. 平衡二叉树
- 226. 翻转二叉树
- 543.二叉树的直径
- 617. 合并二叉树
- 897. 递增顺序搜索树
- 剑指 Offer 32 - II. 从上到下打印二叉树 II
二叉树的遍历分为深度遍历和广度遍历。深度优先遍历有前序、中序、后续三种遍历方法;广度优先遍历即平时所说的层次遍历。因为树的定义本身是递归定义,因此采用递归方法实现树的三种遍历不仅容易理解而且代码更简洁。而对于广度遍历来说需要其他数据结构的支撑才能实现遍历。
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {
}
TreeNode(int val) {
this.val = val;
}
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
- 前序遍历: 根节点-->左子树-->右子树
- 中序遍历:左子树-->跟节点-->右子树
- 后续遍历:左子树-->右子树-->根节点
- 层次遍历:按层遍历即可
以上图为例,前序、中序、后序及层序遍历的结果为:
- 前序遍历:1 2 4 5 7 8 3 6
- 中序遍历:4 2 7 5 8 1 3 6
- 后序遍历:4 7 8 5 2 6 3 1
- 层序遍历:1 2 3 4 5 6 7 8
- 递归法实现
前序遍历的递归实现非常简单,先获取root节点的值并存储到集合,然后分别递归遍历左子树和右子树即可。
List<Integer> list = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root){
if(root == null){
return list;
}
list.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return list;
}
- 迭代法实现
前序遍历的迭代实现需要借助栈的数据结构。首先,将根节点push到栈中,通过while循环判断如果栈中为null,则终止循环。否则,则将根节点pop出栈,取出值,并分别判断右子、左子树是否为null,如果不为null则分别加入到栈中。直至遍历完整棵树为止。
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
result.add(node.val);
if (node.right != null)
stack.push(node.right);
if (node.left != null)
stack.push(node.left);
}
return result;
}
- 递归法实现
定义inorderTraversal(root) 表示当前遍历到 root 节点的答案,按照中序遍历定义,只要递归调用 inorderTraversal(root.left),然后将root节点的值加入集合,再递归调用inorderTraversal(root.right)来遍历 root 节点的右子树即可。递归终止条件为碰到空节点。
时间复杂度: O(n) 。其中 n 为二叉树节点个数。二叉树遍历每个节点会被访问一次,且只会被访问一次。
空间复杂度: O(n) 。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下回达到O(n)的级别。
List<Integer> result = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root == null){
return result;
}
inorderTraversal(root.left);
result.add(root.val);
inorderTraversal(root.right);
return result;
}
- 迭代法实现
和前序遍历的迭代实现类似,区别是考察到当期节点时,并不直接输出该节点的值,而是将其入栈,然后迭代遍历其左节点,并将这些节点都加入栈。直至遍历到左节点为空,则从栈pop出一个节点,并读取该节点的值,然后获取其右节点,将右节点再次进行上述的迭代遍历,直至整棵树遍历完成。
public List<Integer> inorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
List<Integer> result = new ArrayList<>();
TreeNode treeNode = root;
while(treeNode != null|| !stack.isEmpty()){
if(treeNode != null){
stack.push(treeNode);
treeNode = treeNode.left;
} else {
TreeNode node = stack.pop();
result.add(node.val);
treeNode = node.right;
}
}
return result;
}
中序遍历模板
while(node!= null || !stack.isEmpty){
if(node != null){ // 这一步主要是为了遍历到树的左叶子节点
// 1.node 入栈
// 2.让node等于node的左节点
} else {
// 此时树的最左节点全部被加入栈中
// 1.pop出栈得到最左叶子节点并打印叶子节点的值
// 2.获取这个节点的右节点
}
}
- 递归法实现
List<Integer> list = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
if(root == null) {
return list;
}
postorderTraversal(root.left);
postorderTraversal(root.right);
list.add(root.val);
return list;
}
- 迭代法实现
public List<Integer> postorderTraversal2(TreeNode root) {
List<Integer> result = new ArrayList<>();
if(root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root,right = null;
while(node != null || !stack.isEmpty()){
if(node != null){
stack.push(node);
node = node.left;
} else {
node = stack.peek();
if(node.right != null && node.right != right){
node = node.right;
} else {
result.add(node.val);
right = node;
stack.pop();
node = null;
}
}
}
return result;
}
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if(root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root,right = null;
while(node != null || !stack.isEmpty()){
if(node != null){
stack.push(node);
node = node.left;
} else {
node = stack.peek();
if(node.right != null && node.right != right){
node = node.right;
} else {
result.add(node.val);
right = node;
stack.pop();
node = null;
}
}
}
return result;
}
可以使用队列的结构实现二叉树的层序遍历。将每一层的节点放入队列中,得到这一层的节点个数,然后通过for循环遍历这一层的所有节点。通过queue的poll方法将队列的头结点取出,并读取节点的值,然后查看其是否有左右子节点。如果有则通过offer分别将左节点与右节点加入队列。for循环遍历完之后,队列中剩下的就只有下一层的元素了。通过while循环继续遍历下一层即可。
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null)
return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
ArrayList<Integer> levelList = new ArrayList<>();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
levelList.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
result.add(levelList);
}
return result;
}
public class LeetCode100 {
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
} else if (p == null || q == null || p.val != q.val) {
return false;
} else {
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}
}
public class LeetCode101 {
public boolean isSymmetric(TreeNode root) {
return checkSubTree(root, root);
}
private boolean checkSubTree(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
return p.val == q.val && checkSubTree(p.right, q.left) && checkSubTree(p.left, q.right);
}
}
- 解法一:深度优先递归查找
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
} else {
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
- 解法二:广度优先
public int maxDepth2(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int level = 0;
while (!queue.isEmpty()) {
int n = queue.size();
for (int i = 0; i < n; i++) {
TreeNode node = queue.poll();
if (node != null) {
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
level++;
}
return level;
}
public boolean isBalanced(TreeNode root) {
if (root == null || (root.right == null && root.left == null)) {
return true;
}
return Math.abs(getTreeDepth(root.left) - getTreeDepth(root.right)) <= 1 && isBalanced(root.right) && isBalanced(root.left);
}
private int getTreeDepth(TreeNode node) {
if (node == null) {
return 0;
}
return Math.max(getTreeDepth(node.right), getTreeDepth(node.left)) + 1;
}
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode leftNode = invertTree(root.left);
TreeNode rightNode = invertTree(root.right);
root.left = rightNode;
root.right = leftNode;
return root;
}
class Solution {
int res;
public int diameterOfBinaryTree(TreeNode root) {
getTreeDepth(root);
return res;
}
private int getTreeDepth(TreeNode root) {
if (root == null) return 0;
int leftDepth = getTreeDepth(root.left);
int rightDepth = getTreeDepth(root.right);
res = Math.max(res, rightDepth + leftDepth);
return Math.max(leftDepth, rightDepth) + 1;
}
}
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {
return root1;
}
root1.val = root1.val + root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
}
Queue<TreeNode> queue = new LinkedList<>();
public TreeNode increasingBST(TreeNode root) {
doTraversal(root);
TreeNode node = queue.poll();
TreeNode head = node;
while (!queue.isEmpty()) {
node.right = queue.poll();
node.left = null;
node = node.right;
}
node.left = null;
node.right = null;
return head;
}
public void doTraversal(TreeNode root) {
if (root == null) {
return;
}
doTraversal(root.left);
queue.offer(root);
doTraversal(root.right);
}
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> resultList = new ArrayList<>();
if (root == null) {
return resultList;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (node != null) {
if (node.left != null)
queue.offer(node.left);
if (node.right != null) {
queue.offer(node.right);
}
arrayList.add(node.val);
}
}
resultList.add(arrayList);
}
return resultList;
}
- JMM与volatile关键字
- synchronized的实现原理
- synchronized等待与唤醒机制
- AQS的实现原理
- ReentrantLock的实现原理
- ReentrantLock等待与唤醒机制
- CAS、Unsafe类以及Automic并发包
- ThreadLocal的实现原理
- 线程池的实现原理
- Java线程中断机制
- 多线程与并发常见面试题
- Android基础知识汇总
- MVC、MVP与MVVM
- SparseArray实现原理
- ArrayMap的实现原理
- SharedPreferences
- Bitmap
- Activity的启动模式
- Fragment核心原理
- 组件化项目架构搭建
- 组件化WebView架构搭建
- 为什么 Activity.finish() 之后 10s 才 onDestroy ?
- Binder与AIDL
- Binder实现原理
- Android系统启动流程
- InputManagerService
- WindowManagerService
- Choreographer详解
- SurfaceFlinger
- ViewRootImpl
- ActivityManagerService
- APP启动流程
- PMS安装与签名校验
- Dalvik与ART
- 内存优化策略
- UI界面及卡顿优化
- App启动优化
- ANR问题
- 包体积优化
- APK打包流程
- 电池电量优化
- Android屏幕适配
- 线上性能监控1--线上监控切入点
- 线上性能监控2--Matrix实现原理
- Glide实现原理
- OkHttp实现原理
- Retrofit实现原理
- RxJava实现原理
- RxJava中的线程切换与线程池
- LeakCanary实现原理
- ButterKnife实现原理
- ARouter实现原理
- Tinker实现原理
- 2. 两数相加
- 19.删除链表的倒数第 N 个结点
- 21. 合并两个有序链表
- 24. 两两交换链表中的节点
- 61. 旋转链表
- 86. 分隔链表
- 92. 反转链表 II
- 141. 环形链表
- 160. 相交链表
- 206. 反转链表
- 206 反转链表 扩展
- 234. 回文链表
- 237. 删除链表中的节点
- 445. 两数相加 II
- 面试题 02.02. 返回倒数第 k 个节点
- 面试题 02.08. 环路检测
- 剑指 Offer 06. 从尾到头打印链表
- 剑指 Offer 18. 删除链表的节点
- 剑指 Offer 22. 链表中倒数第k个节点
- 剑指 Offer 35. 复杂链表的复制
- 1. 两数之和
- 11. 盛最多水的容器
- 53. 最大子序和
- 75. 颜色分类
- 124.验证回文串
- 167. 两数之和 II - 输入有序数组 -169. 多数元素
- 189.旋转数组
- 209. 长度最小的子数组
- 283.移动0
- 303.区域和检索 - 数组不可变
- 338. 比特位计数
- 448. 找到所有数组中消失的数字
- 643.有序数组的平方
- 977. 有序数组的平方