树形结构是数据结构中最基础的结构之一,而二叉树又是基础中的基础。所以这篇文章我们来介绍介绍二叉树的结构和基本的遍历方法。




1.二叉树的基础知识

二叉树是N个节点的集合,每个节点最多只能有两个节点,每个节点对应的两个子树分别为左子树和右子树。

满二叉树:除在二叉树最下层的节点外,每层的节点都有两个子节点。如下:

wKiom1cd666TzHXBAADE1wAN1l4793.png

完全二叉树:除最后一层外,其他各层的节点数都达到最大个数,且最后一层节点是从左到右的连续存在。如图:

wKiom1cd7WvS6dTnAACs2daZgOA229.png

下面我们来看看完全二叉树的性质,讨论的范围都是顺序存储的情况。假设一个完全二叉树有N个节点,那对于任意一个节点M,有以下性质:

①如果M!=1,则节点M的父节点的编号为M/2。比如上面8和9节点的父节点都是4,8/2=4和9/2=4。

②如果2*M<=N,则节点M的左子树根节点的编号为2*M;如果2*M>N,则无左子树,也无右子树。比如上面2*5<=10,所以左子树根节点为2*5=10。

③如果2*M+1<=N,则节点M的右子树根节点的编号为2*M+1;如果2*M+1>N,则无右子树。比如上面编号为4的节点的右子树为2*4+1=9;而编号为5的节点,由于2*5+1>10,所以无右节点。




2.二叉树的遍历

二叉树的遍历基本上分4种:分层遍历、前序遍历、中序遍历和后序遍历。

①分层遍历

这种遍历方式最简单,也最容易理解,就是从第一层到最后一层,总左到右依次遍历。遍历顺序如下:

wKioL1ceTKWDxtm5AAC57B_RHiY726.png

②先序遍历

先遍历根节点,再遍历左节点,最后遍历右节点。遍历顺序如下:

wKiom1cezL-hLsHsAADBhtnicZA267.png

③中序遍历

先遍左节点,再遍历根节点,最后遍历右节点。遍历顺序如下:

wKiom1cezYPhEMiTAADZKjFvbr4320.png

④后序遍历

先遍左节点,再遍历右节点,最后遍历根节点。遍历顺序如下:
wKioL1cez3Xyj0HuAADCgaL13qA050.png

所谓的先序、中序和后序只是遍历时根节点在‘左右根’三个节点中的顺序。




下面是一个简单的二叉树生成和遍历的代码。

package com.jaeger.binarytree;

import java.util.ArrayList;
import java.util.Scanner;

public class BinaryTreeTest {

	private static Scanner input = new Scanner(System.in);

	@SuppressWarnings("unused")
	TreeNode initRootNode() {
		TreeNode rootNode;
		if ((rootNode = new TreeNode()) != null) {
			System.out.println("输入根节点数据:");
			rootNode.setTreeData(input.next());
			rootNode.setLeftNode(null);
			rootNode.setRightNode(null);
			return rootNode;
		}
		return null;
	}

	void addTreeNode(TreeNode rootNode) {
		TreeNode treeNode;
		TreeNode parentTreeNode;
		int choiceNumber;
		if ((treeNode = new TreeNode()) != null) {
			System.out.println("请输入节点数据:");
			treeNode.setTreeData(input.next());

			System.out.println("请输入父节点数据:");
			parentTreeNode = findTreeNode(rootNode, input.next());
			if (parentTreeNode == null) {
				System.out.println("找不到父节点");
				treeNode = null;
				return;
			}

			do {
				System.out.println("1.添加到左子树\n2.添加到右子树");
				choiceNumber = input.nextInt();
				switch (choiceNumber) {
				case 1:
					if (parentTreeNode.getLeftNode() != null) {
						System.out.println("节点已经有左子树");
					} else {
						parentTreeNode.setLeftNode(treeNode);
					}
					break;
				case 2:
					if (parentTreeNode.getRightNode() != null) {
						System.out.println("节点已经有右子树");
					} else {
						parentTreeNode.setRightNode(treeNode);
					}
					break;
				default:
					System.out.println("无效参数");
				}
			} while (choiceNumber != 1 && choiceNumber != 2);
		}
	}

	TreeNode findTreeNode(TreeNode treeNode, String treeData) {
		TreeNode childTreeNode;
		if (treeNode == null) {
			return null;
		} else {
			if (treeNode.getTreeData().equals(treeData)) {
				return treeNode;
			} else {
				if ((childTreeNode = findTreeNode(treeNode.getLeftNode(), treeData)) != null) {
					return childTreeNode;
				} else if ((childTreeNode = findTreeNode(treeNode.getRightNode(), treeData)) != null) {
					return childTreeNode;
				} else {
					return null;
				}
			}
		}
	}

	//先序遍历
	void DLRTree(TreeNode treeNode) {
		if (treeNode != null) {
			System.out.print(treeNode.getTreeData() + " ");
			DLRTree(treeNode.getLeftNode());
			DLRTree(treeNode.getRightNode());
		}
	}

	//中序遍历
	void LDRTree(TreeNode treeNode) {
		if (treeNode != null) {
			LDRTree(treeNode.getLeftNode());
			System.out.print(treeNode.getTreeData() + " ");
			LDRTree(treeNode.getRightNode());
		}
	}

	//后序遍历
	void LRDTree(TreeNode treeNode) {
		if (treeNode != null) {
			LRDTree(treeNode.getLeftNode());
			LRDTree(treeNode.getRightNode());
			System.out.print(treeNode.getTreeData() + " ");
		}
	}

	//分层遍历
	void levelTree(TreeNode treeNode) {
		ArrayList<TreeNode> treeData = new ArrayList<TreeNode>();
		//当前遍历到哪个节点
		int headIndex = 0;
		TreeNode tempTreeNode = null;
		treeData.add(treeNode);
		while(headIndex < treeData.size()){
			tempTreeNode = treeData.get(headIndex++);
			if(tempTreeNode.getLeftNode() != null){
				treeData.add(tempTreeNode.getLeftNode());
			}
			if(tempTreeNode.getRightNode() != null){
				treeData.add(tempTreeNode.getRightNode());
			}
		}
		for(TreeNode tn : treeData){
			System.out.print(tn.getTreeData() + " ");
		}
	}

	public static void main(String[] args) {
		BinaryTreeTest btt = new BinaryTreeTest();
		TreeNode rootNode = btt.initRootNode();
		int choiceNumber;
		do {
			System.out.println("添加节点");
			System.out.println("0.退出");
			System.out.println("1.添加子节点");
			choiceNumber = input.nextInt();
			switch (choiceNumber) {
			case 0:
				break;
			case 1:
				btt.addTreeNode(rootNode);
				break;
			default:
				System.out.println("无效参数");
			}
		} while (choiceNumber != 0);

		do {
			System.out.println("遍历");
			System.out.println("0.退出");
			System.out.println("1.先序遍历");
			System.out.println("2.中序遍历");
			System.out.println("3.后序遍历");
			System.out.println("4.按层遍历");
			choiceNumber = input.nextInt();
			switch (choiceNumber) {
			case 0:
				break;
			case 1:
				btt.DLRTree(rootNode);
				break;
			case 2:
				btt.LDRTree(rootNode);
				break;
			case 3:
				btt.LRDTree(rootNode);
				break;
			case 4:
				btt.levelTree(rootNode);
				break;
			default:
				System.out.println("无效参数");
			}
		} while (choiceNumber != 0);
	}
	
	private class TreeNode {
		private String treeData;
		private TreeNode leftNode;
		private TreeNode rightNode;

		public String getTreeData() {
			return treeData;
		}

		public void setTreeData(String treeData) {
			this.treeData = treeData;
		}

		public TreeNode getLeftNode() {
			return leftNode;
		}

		public void setLeftNode(TreeNode leftNode) {
			this.leftNode = leftNode;
		}

		public TreeNode getRightNode() {
			return rightNode;
		}

		public void setRightNode(TreeNode rightNode) {
			this.rightNode = rightNode;
		}
	}

}