二叉树的创建和遍历(递归实现)
在一文中介绍了二叉树的基本结构。
在一文中介绍了如何使用递归。
二叉树的结构是递归的,所以创建、遍历也可以通过递归实现。
下面是一颗二叉树:
结点的定义:
public class Node {
Integer value;
Node leftChild;
Node rightChild;
public Node(Integer value) {
this.value = value;
}
}
创建
各个结点的值用一个ArrayList
集合来保存,根据该集合来创建二叉树。
下面按照!这篇文章中的方法分析如何递归地创建一颗二叉树。
第一步:找到大问题是什么?
创建一颗二叉树。
private Node createBinaryTree(ArrayList<Integer> inputList) {
}
第二步:找到最简单问题是什么?满足最简单问题时应该做什么?
「创建一个空二叉树」是最简单的问题,当满足时,直接返回null
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {//最简单问题
return null;
}
}
第三步:找到重复逻辑是什么?
因为我们把每个结点的值都放在ArrayList集合中了,所以,每创建一个二叉树结点,都需要从集合中拿值。
对于每个结点而言,它一定有左孩子和右孩子(上图中结点3的左孩子和右孩子可以看成「值为null的结点」),
所以要确定每个结点的左孩子和右孩子是谁。
所以重复逻辑是:
-
从集合中拿值,创建结点。 -
确定该结点的左孩子和右孩子。
//大问题
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {//最简单问题
return null;
}
Node node = null;//重复逻辑
Integer value = inputList.remove(0);//重复逻辑
if (value != null) {
node = new Node(value);//重复逻辑
node.leftChild = ?;//重复逻辑
node.rightChild = ?;//重复逻辑
}
}
第四步:自己调用自己
先解释一下上个代码片段中的问号。
要确定一个结点的左孩子和右孩子是谁,其实就是一个赋值操作,那么就一定要先有一些可选的结点。
比如说,如果我们要确定结点1的左右孩子,那么结点2、结点5就必须已经被创建出来了,这样才能进行赋值操作。
那么如何在进行赋值操作之前创建结点2、结点5呢?答案是自己调用自己。
我们可以把结点2、结点5看成另一颗二叉树的根结点,只要我们创建好以结点2或结点5为根结点的二叉树,那么结点2和结点5自然就被创建出来了。
确定结点2和结点5的左右孩子同理,这样一直分解下去,直到分解成最简单问题,或者从集合中拿到null为止。
注意:自己调用自己时参数的变小是通过inputList.remove(0)
实现的。
//大问题
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {//最简单问题
return null;
}
Node node = null;//重复逻辑
Integer value = inputList.remove(0);//重复逻辑
if (value != null) {
node = new Node(value);//重复逻辑
node.leftChild = createBinaryTree(inputList);//重复逻辑,自己调用自己
node.rightChild = createBinaryTree(inputList);//重复逻辑,自己调用自己
}
}
第五步:返回
返回的是根结点,该根结点被确定为左孩子或右孩子,从而构成一颗更大的二叉树,直到满足最大问题的那颗二叉树被创建成功,此时返回的根结点是真正的解。
//大问题
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {//最简单问题
return null;
}
Node node = null;//重复逻辑
Integer value = inputList.remove(0);//重复逻辑
if (value != null) {
node = new Node(value);//重复逻辑
node.leftChild = createBinaryTree(inputList);//重复逻辑,自己调用自己
node.rightChild = createBinaryTree(inputList);//重复逻辑,自己调用自己
}
return node;//返回
}
遍历
先序遍历
第一步:找到大问题是什么?
先序遍历一颗二叉树,打印出每个结点的值。
public void preOrderTraveral(Node node) {
}
第二步:找到最简单问题是什么?满足最简单问题时应该做什么?
「遍历一颗空二叉树」是最简单问题,此时任何操作都不用做。
public void preOrderTraveral(Node node) {
if (node == null) {//最简单问题
return;
}
}
第三步:找到重复逻辑是什么?
打印每个结点的值
public void preOrderTraveral(Node node) {
if (node == null) {//最简单问题
return;
}
System.out.print(node.value);//重复逻辑
}
第四步:自己调用自己
先序遍历的过程:
-
遍历根结点 -
先序遍历左子树 -
先序遍历右子树
public void preOrderTraveral(Node node) {
if (node == null) {//最简单问题
return;
}
System.out.print(node.value);//重复逻辑
preOrderTraversal(node.leftChild);//自己调用自己
preOrderTraversal(node.rightChild);//自己调用自己
}
自己调用自己时参数通过node.leftChild
、node.rightChild
不断变小
第五步:返回
不需要返回值。
中序遍历和后序遍历同理
完整代码
//二叉树结点
public class Node {
Integer value;
Node leftChild;
Node rightChild;
public Node(Integer value) {
this.value = value;
}
}
//二叉树
public class BinaryTree {
private Node root;
public Node getRoot() {
return root;
}
public BinaryTree(ArrayList<Integer> inputList) {
Node root = createBinaryTree(inputList);
this.root = root;
}
//创建二叉树
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {
return null;
}
Node node = null;
Integer value = inputList.remove(0);
if (value != null) {
node = new Node(value);
node.leftChild = createBinaryTree(inputList);
node.rightChild = createBinaryTree(inputList);
}
return node;
}
//先序遍历
public void preOrderTraversal(Node node) {
if (node == null) {
return;
}
System.out.print(node.value);
preOrderTraversal(node.leftChild);
preOrderTraversal(node.rightChild);
}
//中序遍历
public void inOrderTraversal(Node node) {
if (node == null) {
return;
}
inOrderTraversal(node.leftChild);
System.out.print(node.value);
inOrderTraversal(node.rightChild);
}
//后序遍历
public void postOrderTraversal(Node node) {
if (node == null) {
return;
}
postOrderTraversal(node.leftChild);
postOrderTraversal(node.rightChild);
System.out.print(node.value);
}
}
//测试
public static void main(String[] args) {
List<Integer> list = Arrays.asList(new Integer[]{1, 2, 3, null, null, 4, null, null, 5, null, 6});
ArrayList inputList = new ArrayList(list);
BinaryTree binaryTree = new BinaryTree(inputList);
Node root = binaryTree.getRoot();
System.out.print("先序遍历:");
binaryTree.preOrderTraversal(root);
System.out.print("\n中序遍历:");
binaryTree.inOrderTraversal(root);
System.out.print("\n后序遍历:");
binaryTree.postOrderTraversal(root);
}