本文介绍了如何使用递归创建和遍历二叉树

树的基本概念和术语总结一文中介绍了二叉树的基本结构。

不知道怎样用递归?按步骤来!一文中介绍了如何使用递归。

二叉树的结构是递归的,所以创建、遍历也可以通过递归实现。

下面是一颗二叉树:

在这里插入图片描述

结点的定义:

  1. public class Node {
  2. Integer value;
  3. Node leftChild;
  4. Node rightChild;
  5. public Node(Integer value) {
  6. this.value = value;
  7. }
  8. }

在这里插入图片描述

各个结点的值用一个ArrayList集合来保存,根据该集合来创建二叉树。

按照不知道怎样用递归?按步骤来!中的方法分析如何递归地创建一颗二叉树。

第一步:找到大问题是什么?

创建一颗二叉树。

  1. private Node createBinaryTree(ArrayList<Integer> inputList) {
  2. }

第二步:找到最简单问题是什么?满足最简单问题时应该做什么?

「创建一个空二叉树」是最简单的问题,当满足时,直接返回null

  1. private Node createBinaryTree(ArrayList<Integer> inputList) {
  2. if (inputList == null || inputList.isEmpty()) {//最简单问题
  3. return null;
  4. }
  5. }

第三步:找到重复逻辑是什么?

因为我们把每个结点的值都放在ArrayList集合中了,所以,每创建一个二叉树结点,都需要从集合中拿值。

对于每个结点而言,它一定有左孩子和右孩子(上图中结点3的左孩子和右孩子可以看成「值为null的结点」),

所以要确定每个结点的左孩子和右孩子是谁。

所以重复逻辑是:

  1. 从集合中拿值,创建结点。
  2. 确定该结点的左孩子和右孩子。
  1. //大问题
  2. private Node createBinaryTree(ArrayList<Integer> inputList) {
  3. if (inputList == null || inputList.isEmpty()) {//最简单问题
  4. return null;
  5. }
  6. Node node = null;//重复逻辑
  7. Integer value = inputList.remove(0);//重复逻辑
  8. if (value != null) {
  9. node = new Node(value);//重复逻辑
  10. node.leftChild = ?;//重复逻辑
  11. node.rightChild = ?;//重复逻辑
  12. }
  13. }

第四步:自己调用自己

先解释一下上个代码片段中的问号。

要确定一个结点的左孩子和右孩子是谁,其实就是一个赋值操作,那么就一定要先有一些可选的结点

比如说,如果我们要确定结点1的左右孩子,那么结点2、结点5就必须已经被创建出来了,这样才能进行赋值操作。

那么如何在进行赋值操作之前创建结点2、结点5呢?答案是自己调用自己。

我们可以把结点2、结点5看成另一颗二叉树的根结点,只要我们创建好以结点2或结点5为根结点的二叉树,那么结点2和结点5自然就被创建出来了。

确定结点2和结点5的左右孩子同理,这样一直分解下去,直到分解成最简单问题,或者从集合中拿到null为止。

注意:自己调用自己时参数的变小是通过inputList.remove(0)实现的。

  1. //大问题
  2. private Node createBinaryTree(ArrayList<Integer> inputList) {
  3. if (inputList == null || inputList.isEmpty()) {//最简单问题
  4. return null;
  5. }
  6. Node node = null;//重复逻辑
  7. Integer value = inputList.remove(0);//重复逻辑
  8. if (value != null) {
  9. node = new Node(value);//重复逻辑
  10. node.leftChild = createBinaryTree(inputList);//重复逻辑,自己调用自己
  11. node.rightChild = createBinaryTree(inputList);//重复逻辑,自己调用自己
  12. }
  13. }

第五步:返回

返回的是根结点,该根结点被确定为左孩子或右孩子,从而构成一颗更大的二叉树,直到满足最大问题的那颗二叉树被创建成功,此时返回的根结点是真正的解。

  1. //大问题
  2. private Node createBinaryTree(ArrayList<Integer> inputList) {
  3. if (inputList == null || inputList.isEmpty()) {//最简单问题
  4. return null;
  5. }
  6. Node node = null;//重复逻辑
  7. Integer value = inputList.remove(0);//重复逻辑
  8. if (value != null) {
  9. node = new Node(value);//重复逻辑
  10. node.leftChild = createBinaryTree(inputList);//重复逻辑,自己调用自己
  11. node.rightChild = createBinaryTree(inputList);//重复逻辑,自己调用自己
  12. }
  13. return node;//返回
  14. }

第一步:找到大问题是什么?

先序遍历一颗二叉树,打印出每个结点的值。

  1. public void preOrderTraveral(Node node) {
  2. }

第二步:找到最简单问题是什么?满足最简单问题时应该做什么?

「遍历一颗空二叉树」是最简单问题,此时任何操作都不用做。

  1. public void preOrderTraveral(Node node) {
  2. if (node == null) {//最简单问题
  3. return;
  4. }
  5. }

第三步:找到重复逻辑是什么?

打印每个结点的值

  1. public void preOrderTraveral(Node node) {
  2. if (node == null) {//最简单问题
  3. return;
  4. }
  5. System.out.print(node.value);//重复逻辑
  6. }

第四步:自己调用自己

先序遍历的过程:

  1. 遍历根结点
  2. 先序遍历左子树
  3. 先序遍历右子树
  1. public void preOrderTraveral(Node node) {
  2. if (node == null) {//最简单问题
  3. return;
  4. }
  5. System.out.print(node.value);//重复逻辑
  6. preOrderTraversal(node.leftChild);//自己调用自己
  7. preOrderTraversal(node.rightChild);//自己调用自己
  8. }

自己调用自己时参数通过node.leftChildnode.rightChild不断变小

第五步:返回

不需要返回值。

  1. //二叉树结点
  2. public class Node {
  3. Integer value;
  4. Node leftChild;
  5. Node rightChild;
  6. public Node(Integer value) {
  7. this.value = value;
  8. }
  9. }
  1. //二叉树
  2. public class BinaryTree {
  3. private Node root;
  4. public Node getRoot() {
  5. return root;
  6. }
  7. public BinaryTree(ArrayList<Integer> inputList) {
  8. Node root = createBinaryTree(inputList);
  9. this.root = root;
  10. }
  11. //创建二叉树
  12. private Node createBinaryTree(ArrayList<Integer> inputList) {
  13. if (inputList == null || inputList.isEmpty()) {
  14. return null;
  15. }
  16. Node node = null;
  17. Integer value = inputList.remove(0);
  18. if (value != null) {
  19. node = new Node(value);
  20. node.leftChild = createBinaryTree(inputList);
  21. node.rightChild = createBinaryTree(inputList);
  22. }
  23. return node;
  24. }
  25. //先序遍历
  26. public void preOrderTraversal(Node node) {
  27. if (node == null) {
  28. return;
  29. }
  30. System.out.print(node.value);
  31. preOrderTraversal(node.leftChild);
  32. preOrderTraversal(node.rightChild);
  33. }
  34. //中序遍历
  35. public void inOrderTraversal(Node node) {
  36. if (node == null) {
  37. return;
  38. }
  39. inOrderTraversal(node.leftChild);
  40. System.out.print(node.value);
  41. inOrderTraversal(node.rightChild);
  42. }
  43. //后序遍历
  44. public void postOrderTraversal(Node node) {
  45. if (node == null) {
  46. return;
  47. }
  48. postOrderTraversal(node.leftChild);
  49. postOrderTraversal(node.rightChild);
  50. System.out.print(node.value);
  51. }
  52. }
  1. //测试
  2. public static void main(String[] args) {
  3. List<Integer> list = Arrays.asList(new Integer[]{1, 2, 3, null, null, 4, null, null, 5, null, 6});
  4. ArrayList inputList = new ArrayList(list);
  5. BinaryTree binaryTree = new BinaryTree(inputList);
  6. Node root = binaryTree.getRoot();
  7. System.out.print("先序遍历:");
  8. binaryTree.preOrderTraversal(root);
  9. System.out.print("\n中序遍历:");
  10. binaryTree.inOrderTraversal(root);
  11. System.out.print("\n后序遍历:");
  12. binaryTree.postOrderTraversal(root);
  13. }

如有错误,还请指正。


文章首发于微信公众号『行人观学』
在这里插入图片描述

版权声明:本文为xingrenguanxue原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xingrenguanxue/p/13163169.html