目录
节点类:
@AllArgsConstructor
public class TreeNode {
Object val;
TreeNode left;
TreeNode right;
}
测试类树结构
测试代码:
@Test
public void testTree(){
TreeNode root = new TreeNode("A", new TreeNode("B", new TreeNode("D", null, null), new TreeNode("E", null, null))
, new TreeNode("C", new TreeNode("F", null, null), null));
}
二叉树的前序遍历:
打印节点顺序:根节点→左子树→右子树
访问顺序图:A→B→D→E→C→F
递归代码:
/**
* 先序遍历(递归)
* @param root 当前树节点
*/
public void preOrderRecursive(TreeNode root) {
if (root == null) {
return;
}
// 输出当前节点
System.out.print("->" + root.val);
preOrderRecursive(root.left);
preOrderRecursive(root.right);
}
非递归:将每个当前的右节点与左节点分别入栈(注意要右节点先,因为栈是先进后出,我这里只用双端队列当作栈。)
非递归代码:
/**
* 先序遍历(非递归)
* @param root 当前树节点
*/
public void preOrderNonRecursive(TreeNode root){
Deque<TreeNode> deque = new LinkedList<>();
if(root != null){
deque.addFirst(root);
}
while (!deque.isEmpty()){
// 出栈并且保存节点
TreeNode cur = deque.pollFirst();
System.out.print("->"+cur.val);
if(cur.right != null){
deque.addFirst(cur.right);
}
if(cur.left != null){
deque.addFirst(cur.left);
}
}
}
输出结果:
中序遍历:
打印节点顺序:左子树→根节点→右子树
访问顺序图:D→B→E→A→F→C
(其特点:打印顺序为每个节点垂直到同一条水平线上,即为打印顺序)
递归代码:
/**
* 中序遍历(递归)
*
* @param root 当前根节点
*/
public void mediumOrderRecursion(TreeNode root) {
if (root == null) {
return;
}
mediumOrderRecursion(root.left);
System.out.print("->" + root.val);
mediumOrderRecursion(root.right);
}
非递归写法:先通过栈存进每个节点的左子树。然后在从栈中取出(此时栈取得节点应为底层的因为栈是后进先出)。然后对每个取出的节点取其的右节点。如果存在则将其打印,这样直到栈为空,则为所要输出结果。
非递归代码:
/**
* 中序遍历(非递归)
* @param root 当前节点
*/
public void mediumOrderNonRecursion(TreeNode root){
Deque<TreeNode> deque = new LinkedList<>();
while(root != null || !deque.isEmpty()){
while (root != null){
deque.addFirst(root);
root = root.left;
}
if(!deque.isEmpty()){
TreeNode cur = deque.pollFirst();
System.out.print("->"+cur.val);
root = cur.right;
}
}
}
输出结果:
后序遍历:
访问节点顺序:左子树→右子树→根节点
访问顺序图:D→E→B→F→C→A
递归代码:
/**
* 后序遍历(递归)
* @param root 当前节点
*/
public void postorderRecursive(TreeNode root) {
if (root == null) {
return;
}
postorderRecursive(root.left);
postorderRecursive(root.right);
System.out.print("->" + root.val);
}
非递归:需要两个栈,一个栈调整节点顺序,一个栈储存最后的结果。构建思路推荐从最简单的两层的满二叉树考虑。
非递归代码:
/**
* 后序遍历(非递归)
* @param root 当前节点
*/
public void postorderNonRecursive(TreeNode root){
Deque<TreeNode> tempDeque = new LinkedList<>();
Deque<TreeNode> resDeque = new LinkedList<>();
if(root != null){
tempDeque.addFirst(root);
}
while(!tempDeque.isEmpty()){
root = tempDeque.pollFirst();
resDeque.addFirst(root);
if(root.left != null){
tempDeque.addFirst(root.left);
}
if(root.right != null){
tempDeque.addFirst(root.right);
}
}
while(!resDeque.isEmpty()){
System.out.print("->"+(resDeque.pollFirst().val));
}
}
输出结果:
层次遍历
打印节点顺序:A > B > C > D > E > F
BFS写法:用一个队列对每一层的节点进行存储
BFS代码:
/**
* BFS层次遍历
*/
public List<List<Object>> levelTraversal(TreeNode root){
// 用来遍历节点的队列
Deque<TreeNode> deque = new LinkedList<>();
if(root == null){
return new ArrayList<>();
}
deque.addFirst(root);
int count = deque.size();
List<List<Object>> res = new ArrayList<>();
while(!deque.isEmpty() ) {
ArrayList<Object> temp = new ArrayList<>();
while(count > 0){
TreeNode cur = deque.pollLast();
temp.add(cur.val);
count--;
if(cur.left != null){
deque.addFirst(cur.left);
}
if(cur.right != null){
deque.addFirst(cur.right);
}
}
count = deque.size();
res.add(temp);
}
return res;
}
DFS写法:将储存结果的列表当作引用传进去,并记下此时的节点的层数,也当作引用传进去。这样每次递归根据层数赋值就可得到结果。
DFS代码:
public List<List<Object>> levelTraversalDFS(TreeNode root) {
List<List<Object>> res = new ArrayList<>();
helpLevel(0, root, res);
return res;
}
public void helpLevel(int level, TreeNode root, List<List<Object>> res) {
if (root == null) {
return;
}
// 如果层数大于等于列表说明已经过了当前层数
if (level >= res.size()) {
res.add(new ArrayList<Object>());
}
res.get(level).add(root.val);
helpLevel(level + 1, root.left, res);
helpLevel(level + 1, root.right, res);
}