Leetcode Tags(13)Tree
1.前序、中序、后序递归方式遍历二叉树
public void preOrderRecur(Node T) {
if (T != null) {
System.out.print(T.val + " ");
preOrderRecur(T.left);
preOrderRecur(T.right);
}
}
public void inOrderRecur(Node T) {
if (T != null) {
inOrderRecur(T.left);
System.out.print(T.val + " ");
inOrderRecur(T.right);
}
}
public void postOrderRecur(Node T) {
if (T != null) {
postOrderRecur(T.left);
postOrderRecur(T.right);
System.out.print(T.val + " ");
}
}
前、中、后序递归遍历二叉树
2.前序非递归遍历二叉树
- 创建一个栈stack,并将头结点T压入stack中
- 从stack中弹出栈顶结点并访问,然后将栈顶结点的非空右孩子入栈,将栈顶结点的非空左孩子入栈
- 重复上述过程,直到stack为空
public void preOrderUnRecur(Node T) {
if (T != null) {
Stack<Node> stack = new Stack<>();
stack.push(T);
while (!stack.isEmpty()) {
T = stack.pop();
System.out.print(T.val + " ");
if (T.right != null) stack.push(T.right);
if (T.left != null) stack.push(T.left);
}
}
}
分析执行过程:
A
/ \
B C
/ \ / \
D E F G
/ / \
H I J
\
K
结点A入栈,弹出并打印,C入栈、B入栈,此时栈:C B(栈顶)
结点B弹出并打印,E入栈,D入栈,此时:C E D
结点D弹出并打印,H入栈,此时:C E H
结点H弹出并打印,K入栈,此时:C E K
结点K弹出并打印,此时:C E
结点E弹出并打印,此时:C
结点C弹出并打印,G入栈,F入栈,此时:G F
结点F弹出并打印,I入栈,此时:G I
结点I弹出并打印,此时:G
结点G弹出并打印,J入栈,此时:J
结点J弹出并打印,此时:
→A B D H K E C F I G J
前序非递归遍历过程分析
变体1:N-ary树前序遍历非递归
public List<Integer> preOrderUnRecur(N_aryTreeNode root) {
List<Integer> list = new ArrayList<>();
if (root != null) {
Stack<N_aryTreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
root = stack.pop();
list.add(root.val);
if (root.children != null) {
for (int i = root.children.size() - 1; i >= 0; i--) {
stack.push(root.children.get(i));
}
}
}
}
return list;
}
3.中序非递归遍历二叉树
- 创建一个栈对象,初始时T为根结点
- T入栈,对于以T为头的整棵子树来说,依次把左边界入栈,即不断地令T=T.left
- 不断重复上一步,直到T为null,此时弹出栈顶结点并打印,然后令T=T.right
- 当栈为空且T为null时,整个过程执行完毕。
public void inOrderUnRecur(Node T) {
if (T != null) {
Stack<Node> stack = new Stack<>();
while (!stack.isEmpty() || T != null) {
if (T != null) {
stack.push(T);
T = T.left;
} else {
T = stack.pop();
System.out.print(T.val + " ");
T = T.right;
}
}
}
}
分析执行过程:
A
/ \
B C
/ \ / \
D E F G
/ / \
H I J
\
K T为A,A入栈,此时:A,T为B
T为B,B入栈,此时:A B,T为D
T为D,D入栈,此时:A B D,T为H
T为H,H入栈,此时:A B D H,T为null
H出栈打印H,T为K,此时:A B D
K入栈,T为null
K出栈打印K,T为null
D出栈打印D,T为null
B出栈打印B,T为E
E入栈,T为null
E出栈打印E,T为null
A出栈打印A,T为C
T为C,C入栈,此时:C,T为F
T为F,F入栈,此时:C F,T为I
T为I,I入栈,此时:C F I,T为null
I出栈打印I,T为null
F出栈打印F,T为null
C出栈打印C,T为G
T为G,G入栈,此时:G,T为null
G出栈打印G,T为J
T为J,J入栈,此时:J,T为null
J出栈打印J,此时: ,并且T为null,程序停止
→H K D B E A I F C G J
中序非递归遍历过程
4.后序非递归遍历二叉树
- 创建一个栈对象,将头结点T入栈,同时在整个流程中,T代表最近一次弹出并打印的结点,c代表栈顶结点,初始时T为头结点,c为null
- 如果栈不为空,每次令c为栈顶结点但是不弹出,有下面的三种情况:
- 如果c的左孩子不为null,并且T不等于c的左孩子,也不等于c的右孩子,则把c的左孩子压入栈中。这是因为:由于T表示最近一次弹出并打印的结点,如果T等于栈顶结点c的左孩子或者是右孩子,说明c的左子树与右子树已经打印完毕,此时不应该将c的左孩子放入栈中。否则,说明c的左子树还没被处理过,那么此时将c的左孩子入栈。(每次都是左子树先入栈,如果c的右孩子等于T,那么说明左子树一定先于右子树之前已经访问完成,因此不用再管左子树,即不用入栈左子树)
- 如果条件1不成立,并且c的右孩子不为null,T不等于c的右孩子,则把c的右孩子入栈。这是因为:如果是T等于c的右孩子,说明c的右子树已经打印完毕,此时不应该再将c的右孩子放入栈中。否则,说明c的右子树还没被处理过,此时将c的右孩子入栈
- 如果条件1和条件2都不成立,说明c的左子树和右子树都已经打印完毕,从栈中弹出c,并且令T为c
- 重复上述步骤,一致到栈空为止。
public void postOrderUnRecur(Node T) {
if (T != null) {
Stack<Node> stack = new Stack<>();
stack.push(T);
Node c = null;
while (!stack.isEmpty()) {
c = stack.peek();
if (c.left != null && T != c.left && T != c.right) {
stack.push(c.left);
} else if (c.right != null && T != c.right) {
stack.push(c.right);
} else {
System.out.print(stack.pop().val + " ");
T = c;
}
}
}
}
分析执行过程:
A
/ \
B C
/ \ / \
D E F G
/ / \
H I J
\
K T为A,A入栈,此时:A,c=null
T=A,c=A,1if:B入栈,此时:A B
T=A,c=B,1if:D入栈,此时:A B D
T=A,c=D,1if:H入栈,此时:A B D H
T=A,c=H,2if:K入栈,此时:A B D H K
T=A,c=K,3else:打印K,T为K,此时:A B D H
T为K,c=H,3else:打印H,T为H,此时:A B D
T为H,c=D,3else:打印D,T为D,此时:A B
T为D,c=B,2if:E入栈,此时:A E
T为D,c=E,3else:打印E,T为E,此时:A
T为E,c=A,2if:C入栈,此时:A C
T为B,c=C,1if:F入栈,此时:A C F
T为B,c=F,1if:I入栈,此时:A C F I
T为B,c=I,3else:打印I,T为I,此时:A C F
T为I,c=F,3else:打印F,T为F,此时:A C
T为F,c=C,2if:G入栈,此时:A C G
T为F,c=G,2if:J入栈,此时:A C G J
T为F,c=J,3else,打印J,T为J,此时:A C G
T为K,c=G,3else,打印G,T为G,此时:A C
T为G,c=C,3else:打印C,T为C,此时:A
T为C,c=A,3else:打印A,T为A,此时:
→K H D E B I F J G C A
后序非递归遍历过程
变体0:后序递归遍历N-ary树
public void postOrderTraverse(N_aryTreeNode root) {
if (root != null) {
if (root.children != null) {
for(N_aryTreeNode node : root.children) {
postOrderTraverse(node);
}
}
System.out.print(root.val + " ");
}
}
变体1:后序非递归遍历N-ary树(注意到倒着入栈,例如3 2 4入栈时要按照4 2 3的顺序入栈)
后序遍历结果为:5 6 3 2 4 1
方法1:慢一点的方法
public void postOrderUnRecur(N_aryTreeNode root) {
if (root != null) {
Stack<N_aryTreeNode> stack = new Stack<>();
stack.push(root);
N_aryTreeNode c = null;
while (!stack.isEmpty()) {
c = stack.peek();
if (c.children != null && !c.children.contains(root)) {
for (int i = c.children.size() - 1; i >= 0; i--) {
stack.push(c.children.get(i));
}
} else {
System.out.print(stack.pop().val + " ");
root = c;
}
}
}
}
方法二:更快的方法:巧妙地使用了Collections.reverse(list);这个技巧
public List<Integer> postOrderUnRecur2(N_aryTreeNode root) {
List<Integer> list = new ArrayList<>();
if (root == null) return list;
Stack<N_aryTreeNode> stack = new Stack<>();
stack.add(root);
while (!stack.isEmpty()) {
root = stack.pop();
list.add(root.val);
if (root.children != null) {
for (N_aryTreeNode n_aryTreeNode : root.children) {
stack.add(n_aryTreeNode);
}
}
}
Collections.reverse(list);
return list;
}
5.层序非递归遍历二叉树(BFS)
- 创建一个队列对象, 根结点入队列
- 如果队列非空,则将队列头结点弹出并访问,然后将该结点的非空左孩子入队列,再将队列的非空右孩子入队列,
- 重复上述过程
public void levelOrderUnRecur(Node T) {
if (T != null) {
Queue<Node> queue = new ArrayDeque<>();
queue.add(T);
while (!queue.isEmpty()) {
T = queue.poll();
System.out.print(T.val + " ");
if (T.left != null) queue.add(T.left);
if (T.right != null) queue.add(T.right);
}
}
}
分析执行过程:
A
/ \
B C
/ \ / \
D E F G
/ / \
H I J
\
K A入队列,此时:A
A出队列并打印,T为A,B入队列,C入队列,此时:C (队列头)
B出队列并打印,T为B,D入队列,E入队列,此时:E D C
C出队列并打印,T为C,F入队列,G入队列,此时:G F E D
D出队列并打印,T为D,H入队列,此时:H G F E
E出队列并打印,此时:H G F
F出队列并打印,I入队列,此时:I H G
G出队列并打印,J入队列,此时:J I H
H出队列并打印,K入队列,此时:K J I
I出队列并打印,此时:K J
J出队列并打印,此时:K
K出队列并打印,此时:
→A B C D E F G H I J K
层序非递归遍历过程
变体:按照层数打印每一层的数据:
public void printByLevel(Node root) {
if (root == null) return;
Queue<Node> queue = new LinkedList<>();
int level = 1;
Node last = root;
Node nLast = null;
queue.add(root);
System.out.print("Level " + (level++) + " : ");
while (!queue.isEmpty()) {
root = queue.poll();
System.out.print(root.val + " ");
if (root.left != null) {
queue.add(root.left);
nLast = root.left;
}
if (root.right != null) {
queue.add(root.right);
nLast = root.right;
}
if (root == last && !queue.isEmpty()) {
System.out.print("\nLevel " + (level++) + " : ");
last = nLast;
}
}
System.out.println();
}
6.求二叉树的深度
// (后序递归遍历算法)求二叉树的深度
public int getBTDepth(BiTreeNode T) {
if (T != null) {
int lDepth = getBTDepth(T.lchild);
int rDepth = getBTDepth(T.rchild);
return 1 + (lDepth > rDepth ? lDepth : rDepth);
}
return 0;
}
求N-ary 树的最大深度
public int maxDepth(Node root) {
if (root != null) {
int depth = 0;
if (root.children != null) {
for (Node node : root.children) {
depth = Math.max(depth,maxDepth(node));
}
}
return 1 + depth;
}
return 0;
}
求二叉树的最小深度BFS:
public int minDepth(TreeNode root) {
if (root == null) return 0;
int depth = 1;
TreeNode last = root;
TreeNode nLast = null;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
root = queue.poll();
if (root.left == null && root.right == null) return depth;
if (root.left != null) {
queue.offer(root.left);
nLast = root.left;
}
if (root.right != null) {
queue.offer(root.right);
nLast = root.right;
}
if (root == last || queue.isEmpty()) {
last = nLast;
depth++;
}
}
return depth;
}
求二叉树的最小深度DFS:
public int minDepthDFS(TreeNode root) {
if (root == null) return 0;
int lDepth = minDepth(root.left);
int rDepth = minDepth(root.right);
return (lDepth == 0 || rDepth == 0) ? lDepth + rDepth + 1 : Math.min(lDepth, rDepth) + 1;
}
7.求二叉树结点的个数
8.判断两棵二叉树是否相等
如果值相等,那么如果左子树和右子树都相等,就返回true。
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) return true;
if (p != null && q != null) {
if (p.val == q.val) {
if (isSameTree(p.left, q.left)) {
if (isSameTree(p.right, q.right)) {
return true;
}
}
}
}
return false;
}
9.二叉树的查找
10.二叉搜索树BST的查找(迭代和递归两种方法)
public Node searchBSTIteration(Node root, int val) {
while (root != null) {
if (val == root.val) {
return root;
} else if (val < root.val) {
root = root.left;
} else {
root = root.right;
}
}
return null;
}
public Node searchBSTRecursion(Node root, int val) {
if (root == null) return root;
if (root.val == val) {
return root;
} else {
return val < root.val ? searchBSTRecursion(root.left, val) : searchBSTRecursion(root.right, val);
}
}
11.寻找两棵树的叶结点以及判断两棵树的叶结点的值相等
public boolean leafSimilar(TreeNode root1, TreeNode root2) {
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
dfs(root1, list1);
dfs(root2, list2);
return list1.equals(list2);
}
private void dfs(TreeNode root, List<Integer> leafVal) {
if (root != null) {
if (root.left == null && root.right == null) leafVal.add(root.val);
dfs(root.left, leafVal);
dfs(root.right, leafVal);
}
}
12.求左叶子的和
public int sumOfLeftLeaves(TreeNode root) {
int sum = 0;
if (root != null) {
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
root = stack.pop();
if (root.right != null) stack.push(root.right);
if (root.left != null) {
stack.push(root.left);
if (root.left.left == null && root.left.right == null) {
sum += root.left.val;
}
}
}
}
return sum;
}
Leetcode Tags(13)Tree的更多相关文章
- Leetcode Tags(13)Bit Manipulation
一.477.汉明距离总和 输入: , , 输出: 解释: 在二进制表示中,4表示为0100,14表示为1110,2表示为0010.(这样表示是为了体现后四位之间关系) HammingDistance( ...
- Leetcode Tags(6)Math
一.204. Count Primes Count the number of prime numbers less than a non-negative number, n. Input: 10 ...
- Leetcode Tags(1)Linked List
1.知识点回顾 https://www.cnblogs.com/BigJunOba/p/9174206.html https://www.cnblogs.com/BigJunOba/p/9174217 ...
- Leetcode Tags(8)Binary Search
一.475. Heaters 输入: [1,2,3],[2] 输出: 1 解释: 仅在位置2上有一个供暖器.如果我们将加热半径设为1,那么所有房屋就都能得到供暖. 输入: [1,2,3,4],[1,4 ...
- Leetcode Tags(5)Hash Table
一.500. Keyboard Row 给定一个单词列表,只返回可以使用在键盘同一行的字母打印出来的单词. 输入: ["Hello", "Alaska", &q ...
- Leetcode Tags(3)String(TODO)
一.Easy 696 Count Binary Substrings Input: "00110011" Output: 6 Explanation: There are 6 su ...
- Leetcode Tags(2)Array
一.448. Find All Numbers Disappeared in an Array 给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了 ...
- Leetcode Tags(4)Stack & Queue
一.232. Implement Queue using Stacks private Stack<Integer> stack; /** Initialize your data str ...
- 基于MVC4+EasyUI的Web开发框架经验总结(13)--DataGrid控件实现自动适应宽带高度
在默认情况下,EasyUI的DataGrid好像都没有具备自动宽度的适应功能,一般是指定像素宽度的,但是使用的人员计算机的屏幕分辨率可能不一样,因此导致有些地方显示太大或者太小,总是不能达到好的预期效 ...
随机推荐
- JavaScript学习记录
js整理笔记 1.数据类型 2.基本语法 3.js运算符 4.条件语句 5.类型转换 6.函数 7.预编译 8.作用域 9.闭包 10.对象创建方法 11.this 12.dom操作 13.事件 14 ...
- 浅析html+css+javascript之间的关系与作用
三者间的关系 一个基本的网站包含很多个网页,一个网页由html, css和javascript组成. html是主体,装载各种dom元素:css用来装饰dom元素:javascript控制dom元素. ...
- Scrapy项目 - 数据简析 - 实现斗鱼直播网站信息爬取的爬虫设计
一.数据分析截图(weka数据分析截图 2-3个图,作业文字描述) 本次将所爬取的数据信息,如:房间数,直播类别和人气,导入Weka 3.7工具进行数据分析.有关本次的数据分析详情详见下图所示: ...
- uC/OS-III 时钟节拍(一)
时钟节拍就是操作系统的时基,操作系统要实现时间上的管理,必须依赖于时基(时基即时间基准,操作系统的基准时钟). uC/OS-III时钟节拍的实现过程 时钟节拍就是系统以固定的频率产生中断(时基中断), ...
- ef core实现无感知软删除
很多web程序一般的偶不会设计真的物理删除了. 基本上都是在在数据库加一个标记,就得当作已经删除了.同时在查询的时候,过滤已经标记删除的数据 ef core实现软删除是非常简单的,直接在OnModel ...
- git一步步上传自己的项目至github,及仓库更新
一.使用git上传项目到github 首先登陆github账号,选择新建一个库,填写项目名称,描述 创建完成之后,跳转到下面的页面,下面红框中的网址要记住,在后面上传代码的时候需要使用 接下来,我们需 ...
- 项目一:ssm超市订单管理系统
声明:项目参考于课程教材,学习使用,仅在此记录 项目介绍 ssm超市订单管理系统,功能模块有订单管理,供应商管理,用户管理,密码修改,退出系统,管理模块中包括基本的增删改查 集成工具使用idea,基于 ...
- opencv之形态变换
形态变换 在opencv之膨胀与腐蚀中介绍了Dilation/Erosion的原理.建议先读这一篇,搞懂原理. 这样就可以很轻松地理解为什么本文的这些形态变换可以取得相应的效果. 基于此,我们可以组合 ...
- MRP进程起不来, 报错:ORA-00600: internal error code, arguments: [2619], [227424], [], [], [], [], [], [], [], [], [], []
问题背景:客户数据库服务架构为一主一备,某日备库操作系统意外重启,重启后Oracle MRP进程起不来,报错:ORA-00600: internal error code, arguments: [2 ...
- CSS样式手册
字体属性:(font) 大小 {font-size: x-large;}(特大) xx-small;(极小) 一般中文用不到,只要用数值就可以,单位:PX.PD 样式 {font-style: obl ...