二叉树遍历应用

mac2025-06-15  24

文章目录

preorderinorderpostorderlevel orderisComplete完全二叉树(Complete Binary Tree)完全二叉树的性质判断一棵树是否为完全二叉树 height递归迭代

preorder

一路向左走,当访问节点为null时,转向右边

◼ 利用栈实现

设置 node = root循环执行以下操作 如果 node != null ✓ 对 node 进行访问 ✓ 将 node.right 入栈 ✓ 设置 node = node.left 如果 node == null ✓ 如果栈为空,结束遍历 ✓ 如果栈不为空,弹出栈顶元素并赋值给 node

最终前序遍历:6421357,但是总是先从根节点开始访问,所以对于357根节点发现其访问顺序和前序遍历顺序正好相反,则考虑使用栈

栈中保存的全部都是右子节点,所以当第一次出栈时,弹出3,此时处理3和处理6的逻辑相同,继续循环

public void preorder(Visitor<E> visitor) { if (visitor == null || root == null) return; Node<E> node = root; Stack<Node<E>> stack = new Stack<>(); while (true) { if (node != null) { // 访问node节点 if (visitor.visit(node.element)) return; // 将右子节点入栈 if (node.right != null) { stack.push(node.right); } // 向左走 node = node.left; } else if (stack.isEmpty()) { return; } else { // 处理右边 node = stack.pop(); } } } 将 root 入栈循环执行以下操作,直到栈为空 弹出栈顶节点 top,进行访问 将 top.right 入栈 将 top.left 入栈 public void preorder(Visitor<E> visitor) { if (visitor == null || root == null) return; Stack<Node<E>> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { Node<E> node = stack.pop(); // 访问node节点 if (visitor.visit(node.element)) return; if (node.right != null) { stack.push(node.right); } if (node.left != null) { stack.push(node.left); } } }

inorder

中序遍历:1234567,访问节点的顺序6421,但是其遍历顺序正好相反,即仍使用栈

栈中保存的全部都是父节点,所以当第二次出栈时,弹出2,访问其右节点3,处理逻辑与6相同,继续循环

public void inorder(Visitor<E> visitor) { if (visitor == null || root == null) return; Node<E> node = root; Stack<Node<E>> stack = new Stack<>(); while (true) { if (node != null) { stack.push(node); // 向左走 node = node.left; } else if (stack.isEmpty()) { return; } else { node = stack.pop(); // 访问node节点 if (visitor.visit(node.element)) return; // 让右节点进行中序遍历 node = node.right; } } }

postorder

◼ 利用栈实现

将 root 入栈循环执行以下操作,直到栈为空 如果栈顶节点是叶子节点 或者 上一次访问的节点是栈顶节点的子节点(即子节点已经访问过) ✓ 弹出栈顶节点,进行访问 否则 ✓ 将栈顶节点的right、left按顺序入栈

后序遍历:1325476,访问顺序6421,依然相反,即使用栈,注意此时遍历顺序为476,所以入栈顺序为674

public void postorder(Visitor<E> visitor) { if (visitor == null || root == null) return; // 记录上一次弹出访问的节点 Node<E> prev = null; Stack<Node<E>> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { Node<E> top = stack.peek(); if (top.isLeaf() || (prev != null && prev.parent == top)) { prev = stack.pop(); // 访问节点 if (visitor.visit(prev.element)) return; } else { if (top.right != null) { stack.push(top.right); } if (top.left != null) { stack.push(top.left); } } } }

level order

◼ 实现思路:使用队列

将根节点入队循环执行以下操作,直到队列为空 将队头节点 A 出队,进行访问 将 A 的左子节点入队 将 A 的右子节点入队 public void levelOrder(Visitor<E> visitor) { if (root == null || visitor == null) return; Queue<Node<E>> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { Node<E> node = queue.poll(); if (visitor.visit(node.element)) return; // 访问节点 if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } } }

isComplete

完全二叉树(Complete Binary Tree)

◼ 完全二叉树:对节点从上至下、左至右开始编号,其所有编号都能与相同高度的满二叉树中的编号对应

◼ 叶子节点只会出现最后 2 层,最后 1 层的叶子结点都靠左对齐 ◼ 完全二叉树从根结点至倒数第 2 层是一棵满二叉树 ◼ 满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树

完全二叉树的性质

◼ 度为 1 的节点只有左子树

◼ 度为 1 的节点要么是 1 个,要么是 0 个

◼ 同样节点数量的二叉树,完全二叉树的高度最小

◼ 假设完全二叉树的高度为 h ( h ≥ 1 ),那么 至少有 2h − 1 个节点 ( 20 + 21 + 22 + ⋯ + 2h−2 + 1 ) 最多有 2h − 1 个节点( 20 + 21 + 22 + ⋯ + 2h−1 ,满二叉树 ) 总节点数量为 n ✓ 2h − 1 ≤ n < 2h ✓ h − 1 ≤ log2n < h ✓ h = floor( log2n ) + 1

◼ 如果一棵完全二叉树有 768 个节点,求叶子节点的个数 假设叶子节点个数为 n0,度为 1 的节点个数为 n1,度为 2 的节点个数为 n2 总结点个数 n = n0 + n1 + n2,而且 n0 = n2 + 1 (每个节点都有父结点,但是根节点没有) ✓ n = 2n0 + n1 – 1

完全二叉树的 n1 要么为 0,要么为 1 ✓ n1为1时,n = 2n0,n 必然是偶数 ➢ 叶子节点个数 n0 = n / 2,非叶子节点个数 n1 + n2 = n / 2 ✓ n1为0时,n = 2n0 – 1,n 必然是奇数 ➢ 叶子节点个数 n0 = (n + 1) / 2,非叶子节点个数 n1 + n2 = (n – 1) / 2

叶子节点个数 n0 = floor( (n + 1) / 2 ) = ceiling( n / 2 ) 非叶子节点个数 n1 + n2 = floor( n / 2 ) = ceiling( (n – 1) / 2 ) 因此叶子节点个数为 384

◼ 一棵有 n 个节点的完全二叉树(n > 0),从上到下、从左到右对节点从 0 开始进行编号,对任意第 i 个节点 如果 i = 0 ,它是根节点 如果 i > 0 ,它的父节点编号为 floor( (i – 1) / 2 )

如果 2i + 1 ≤ n – 1 ,它的左子节点编号为 2i + 1 如果 2i + 1 > n – 1 ,它无左子节点

如果 2i + 2 ≤ n – 1 ,它的右子节点编号为 2i + 2 如果 2i + 2 > n – 1 ,它无右子节点

按索引找到第一个叶子节点,之后所有节点都是叶子节点

判断一棵树是否为完全二叉树

◼ 如果树为空,返回 false ◼ 如果树不为空,开始层序遍历二叉树(用队列) 如果 node.left!=null,将 node.left 入队 如果 node.left=null && node.right!=null,返回 false (此时度为1,那么为J,只能是left不为空right为空) 如果 node.right!=null,将 node.right 入队 如果 node.right=null ✓ 那么后面遍历的节点应该都为叶子节点,才是完全二叉树 ✓ 否则返回 false 遍历结束,返回 true

public boolean isComplete() { if (root == null) return false; Queue<Node<E>> queue = new LinkedList<>(); queue.offer(root); boolean leaf = false; while (!queue.isEmpty()) { Node<E> node = queue.poll(); if (leaf && !node.isLeaf()) return false; if (node.left != null) { queue.offer(node.left); } else if (node.right != null) { return false; } if (node.right != null) { queue.offer(node.right); } else { // 后面遍历的节点都必须是叶子节点 leaf = true; } } return true; }

height

递归

整个树的高度 = 左子树高度和右子树高度的最大值 + 1(父节点 )

public int height() { return height(root); } private int height(Node<E> node) { if (node == null) return 0; return 1 + Math.max(height(node.left), height(node.right)); }

迭代

层序遍历的特点:每扫描完一层,树的高度+1,即遍历完所有节点,就求出树的高度

要记录每一层的节点数量,每次从队列中出队一个节点,levelsize–

当levelsize==0时意味着该层已经遍历完,要继续访问下一层,并重新赋值levelsize

public int height() { if (root == null) return 0; // 树的高度 int height = 0; // 存储着每一层的元素数量 int levelSize = 1; Queue<Node<E>> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { Node<E> node = queue.poll(); levelSize--; if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } if (levelSize == 0) { // 意味着即将要访问下一层 levelSize = queue.size(); height++; } } return height; }

Reference:小码哥MJ

最新回复(0)