java二叉树递归_二叉树三种遍历递归及非递归实现&层次遍历(Java版)

本文详细介绍了如何使用Java实现二叉树的前序、中序、后序遍历,包括递归和非递归两种方法。还讲解了层次遍历的实现,并附带了相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二叉树基本概念

二叉树是每个结点至多有两颗子树的树,子树有左右之分,其次序不能任意颠倒。

几种特殊二叉树

1) 满二叉树:高度为h,并且含有2h-1个结点的二叉树。即树的每一层都是满的(每层结点数为2(h-1)) 。若对满二叉树编号,从上自下,从左至右,根节点编号为1,则对于编号为i 的结点,如果有双亲,则双亲为⌊i/2⌋,如果有左孩子,左孩子为2i,如果有右孩子,右孩子为2i+1.如下图所示:

9f148caf2535

满二叉树.png

2) 完全二叉树:若1个高度为h,含有n个结点的二叉树,按照从上自下,从左至右的顺序对结点进行编号,1~n的结点与满二叉树一一对应,则该二叉树为完全二叉树。如下图所示:

9f148caf2535

完全二叉树.png

3) 二叉排序树:左子树上所有关键字均小于根节点关键字,右子树所有结点关键字均大于根节点关键字。左子树和右子树又各是一颗二叉排序树。

4) 平衡二叉树:树上任一结点的左子树和右子树的深度之差不超过1。

二叉树遍历

二叉树定义

public class TreeNode {

int val;

TreeNode left;

TreeNode right;

TreeNode(int x) { val = x; }

}

非递归实现借助栈,前序、中序、后序是相对于根节点而言,根据根节点输出的位置,根、左、右为前序遍历;左、根、右为中序遍历;左、右、根为后序遍历。

1) 前序遍历(PreOrder):

如果二叉树为空,则什么也不做;否则:

a)访问根节点;

b)先序遍历左子树;

c)先序遍历右子树。

递归实现:

public List preorderTraversal(TreeNode root) {

List list = new ArrayList<>();

if(root == null)

return list;

helper(root, list);

return list;

}

public void helper(TreeNode root, List list) {

list.add(root.val);

if(root.left != null)

helper(root.left, list);

if(root.right != null)

helper(root.right, list);

}

常规版非递归实现:先将根节点入栈,然后弹出(访问根节点),然后将其右结点入栈,再将其左结点入栈(栈是先进后出,我们需要先访问左结点,所以先将右结点入栈),然后弹出左结点,对左结点也进行同样的操作(右结点先入栈,左结点入栈),直至栈为空并且访问完了所有结点。

public List preorderTraversal(TreeNode root) {

List list = new ArrayList<>();

if(root == null)

return list;

Stack stack = new Stack<>();

stack.push(root);

while(!stack.isEmpty()) {

root = stack.pop();

list.add(root.val);

if(root.right != null)

stack.push(root.right);

if(root.left != null)

stack.push(root.left);

}

return list;

}

简单版非递归实现:与上一种方式的不同是在访问根节点的时机不相同。常规方式是在出栈时访问根节点,将其写入list中,而简单方式是入栈的同时将值写入list,所以我们只要保证按照前序遍历的顺序去遍历二叉树即可。

public List preorderTraversal(TreeNode root) {

List list = new ArrayList<>();

Deque stack = new ArrayDeque<>();

TreeNode node = root;

while(node != null || !stack.isEmpty()) {

if(node != null) {

stack.push(node);

list.add(node.val);

node = node.left;

}else {

node = stack.pop();

node = node.right;

}

}

return list;

}

2) 中序遍历(InOrder):

如果二叉树为空,则什么也不做;否则:

a)中序遍历左子树;

b)访问根节点;

c)中序遍历右子树。

递归实现:

public List inorderTraversal(TreeNode root) {

List list = new ArrayList<>();

if(root == null)

return list;

helper(root, list);

return list;

}

public void helper(TreeNode root, List list) {

if(root.left != null)

helper(root.left, list);

list.add(root.val);

if(root.right != null)

helper(root.right, list);

}

常规版非递归实现:从根节点依次遍历左结点,如果左结点没有被访问过,则入栈,当没有左结点或左结点被访问过的时候,弹出栈顶元素,将其写入list,并将其右结点入栈。重复上述操作。

public List inorderTraversal(TreeNode root) {

List list = new ArrayList<>();

Map map = new HashMap<>();

Stack stack = new Stack<>();

if(root == null)

return list;

stack.push(root);

while(!stack.isEmpty()) {

root = stack.peek();

while(root.left != null) {

if(map.containsKey(root.left))

break;

stack.push(root.left);

root = root.left;

}

root = stack.pop();

list.add(root.val);

map.put(root, 1);

if(root.right != null)

stack.push(root.right);

}

return list;

}

简单版非递归实现:从根节点依次将左结点入栈,当没有左结点的时候,弹出栈顶元素,将其写入list,并将其右结点入栈。重复上述操作。与常规方法相比,省去了查看左子树是否被访问过的步骤,对每个结点,都是先遍历其左子树,所以当访问到该结点的时候,可以确保其左子树已经被访问过了,只需要访问其本身和其右结点即可。

public List inorderTraversal(TreeNode root) {

List list = new ArrayList<>();

Deque stack = new ArrayDeque<>();

TreeNode node = root;

while(node!=null || !stack.isEmpty()) {

if(node != null) {

stack.push(node);

node = node.left;

}else {

node = stack.pop();

list.add(node.val);

node = node.right;

}

}

return list;

}

3) 后序遍历(PostOrder):

如果二叉树为空,则什么也不做;否则:

a)后序遍历左子树;

b)后序遍历右子树;

c)访问根节点。

递归实现:

public List postorderTraversal(TreeNode root) {

List list = new ArrayList<>();

if(root == null)

return list;

helper(root, list);

return list;

}

public void helper(TreeNode root, List list) {

if(root.left != null)

helper(root.left, list);

if(root.right != null)

helper(root.right, list);

list.add(root.val);

}

常规版非递归实现:将根节点左子树入栈,当访问到叶子结点,出栈,查看该叶子结点的父节点是否有右结点,有的话,如果右结点未访问则将其入栈,当一个节点的左右结点都已经访问过或者不包含左右结点,则出栈。

public List postorderTraversal(TreeNode root) {

List list = new ArrayList<>();

Map map = new HashMap<>();

Stack stack = new Stack<>();

if(root == null)

return list;

stack.push(root);

while(!stack.isEmpty()) {

root = stack.peek();

if(root.left == null && root.right == null) {//不含左右结点时,出栈

root = stack.pop();

list.add(root.val);

map.put(root, 1);

}else if((root.left!=null && root.right == null && map.containsKey(root.left))||(root.right != null && root.left == null && map.containsKey(root.right)) || (root.left != null && root.right != null && map.containsKey(root.left) && map.containsKey(root.right))){//包含子节点,但是子节点被访问过,出栈

root = stack.pop();

list.add(root.val);

map.put(root, 1);

}else {

while(root.left != null) {

if(map.containsKey(root.left)) {

break;

}

stack.push(root.left);

root = root.left;

}

if(root.right != null) {

if(map.containsKey(root.right)) {

break;

}

stack.push(root.right);

}

}

}

return list;

}

简单版非递归实现:后序遍历的输出顺序是左、右、根,当我们采用先序遍历的方法,但是先遍历右子树,实现的效果是根、右、左,刚好和后序遍历的结果想法,所以我们通过add(0, node)的方式将顺序反序,达到我们想要的效果。

public List postorderTraversal(TreeNode root) {

List list = new ArrayList<>();

Deque stack = new ArrayDeque<>();

TreeNode node = root;

while(node != null || !stack.isEmpty()) {

if(node != null) {

stack.push(node);

list.add(0, node.val);

node = node.right;

}else {

node = stack.pop();

node = node.left;

}

}

return list;

}

层次遍历:将二叉树按层输出,借助队列实现。借助null来标记一层的结束。当读取到的结点不是null,将该结点的左右结点入队列,当读到null,如果此时队列不空,则继续入null标志新的一层的结尾。

public List> levelOrder(TreeNode root) {

List> list = new ArrayList<>();

if(root == null)

return list;

Queue queue = new LinkedList<>();

queue.add(root);

queue.add(null);

int num = 0;

List tempList = new ArrayList<>();

list.add(tempList);

while(!queue.isEmpty()) {

TreeNode temp = queue.poll();

if(temp != null) {

list.get(num).add(temp.val);

if(temp.left != null)

queue.add(temp.left);

if(temp.right != null)

queue.add(temp.right);

}else {

if(!queue.isEmpty()) {

num++;

tempList = new ArrayList<>();

list.add(tempList);

queue.add(null);

}

}

}

return list;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值