只有光头才能变强。
文本已收录至个人GitHub仓库,欢迎Star: https://github.com/ZhongFuCheng3y/3y
本文撇开一些很是苦涩、难以理解的概念来说讲二叉树,仅入门观看(或复习)....java
首先,咱们来说讲什么是树:git
在现实生活中,咱们通常的树长这个样子的:github
可是在编程的世界中,咱们通常把树“倒”过来看,这样容易咱们分析:编程
通常的树是有不少不少个分支的,分支下又有不少不少个分支,若是在程序中研究这个会很是麻烦。由于原本树就是非线性的,而咱们计算机的内存是线性存储的,太过复杂的话咱们没法设计出来的。数组
所以,咱们先来研究简单又常常用的---> 二叉树数据结构
我就拿上面的图来进行画来说解了:测试
二叉树的意思就是说:每一个节点不能多于有两个儿子,上面的图就是一颗二叉树。this
上面说了,树是由若干个节点组成,节点链接起来就成了树,而节点由一个数据、两个指针组成spa
首先,使用Java类定义节点:设计
public class TreeNode { // 左节点(儿子) private TreeNode lefTreeNode; // 右节点(儿子) private TreeNode rightNode; // 数据 private int value; }
下面咱们就拿这个二叉树为例来构建吧:
为了方便构建,我就给了它一个带参数的构造方法和set、get方法了:
public TreeNode(int value) { this.value = value; }
那么咱们如今就建立了5个节点:
public static void main(String[] args) { //根节点-->10 TreeNode treeNode1 = new TreeNode(10); //左孩子-->9 TreeNode treeNode2 = new TreeNode(9); //右孩子-->20 TreeNode treeNode3 = new TreeNode(20); //20的左孩子-->15 TreeNode treeNode4 = new TreeNode(15); //20的右孩子-->35 TreeNode treeNode5 = new TreeNode(35) }
它们目前的状态是这样子的:
因而下面咱们去把它连起来:
//根节点的左右孩子 treeNode1.setLefTreeNode(treeNode2); treeNode1.setRightNode(treeNode3); //20节点的左右孩子 treeNode3.setLefTreeNode(treeNode4); treeNode3.setRightNode(treeNode5);
链接完以后,那么咱们的树就建立完成了。
上面说咱们的树建立完成了,那怎么证实呢??咱们若是能够像数组同样遍历它(看它的数据),那就说明它建立完成了~
值得说明的是:二叉树遍历有三种方式
先序遍历
中序遍历
后序遍历
以上面的二叉树为例:
10->9->20->15->35
若是是中序遍历:9->10->15->20->35
若是是后序遍历:9->15->35->20->10
一句话总结:先序(根->左->右),中序(左->根->右),后序(左->右->根)。若是访问有孩子的节点,先处理孩子的,随后返回
不管先中后遍历,每一个节点的遍历若是访问有孩子的节点,先处理孩子的(逻辑是同样的)
所以,咱们能够写出这样的先序遍历代码:
/** * 先序遍历 * @param rootTreeNode 根节点 */ public static void preTraverseBTree(TreeNode rootTreeNode) { if (rootTreeNode != null) { //访问根节点 System.out.println(rootTreeNode.getValue()); //访问左节点 preTraverseBTree(rootTreeNode.getLefTreeNode()); //访问右节点 preTraverseBTree(rootTreeNode.getRightNode()); } }
结果跟咱们刚才说的是同样的:
咱们再用中序遍历调用一遍吧:
/** * 中序遍历 * @param rootTreeNode 根节点 */ public static void inTraverseBTree(TreeNode rootTreeNode) { if (rootTreeNode != null) { //访问左节点 inTraverseBTree(rootTreeNode.getLefTreeNode()); //访问根节点 System.out.println(rootTreeNode.getValue()); //访问右节点 inTraverseBTree(rootTreeNode.getRightNode()); } }
结果跟咱们刚才说的是同样的:
有意思的是:经过先序和中序或者中序和后序咱们能够还原出原始的二叉树,可是经过先序和后序是没法还原出原始的二叉树的
上面咱们是手动建立二叉树的,通常地:都是给出一个数组给你,让你将数组变成一个二叉树,此时就须要咱们动态建立二叉树了。
二叉树中还有一种特殊的二叉树:二叉查找树(binary search tree)
定义:当前根节点的左边所有比根节点小,当前根节点的右边所有比根节点大。
每每咱们动态建立二叉树都是建立二叉查找树
假设咱们有一个数组:int[] arrays = {3, 2, 1, 4, 5};
那么建立二叉树的步骤是这样的:
那么咱们的二叉查找树就创建成功了,不管任何一颗子树,左边都比根要小,右边比根要大
咱们的代码实现也很简单,若是比当前根节点要小,那么放到当前根节点左边,若是比当前根节点要大,那么放到当前根节点右边。
由于是动态建立的,所以咱们得用一个类来表示根节点
public class TreeRoot { private TreeNode treeRoot; public TreeNode getTreeRoot() { return treeRoot; } public void setTreeRoot(TreeNode treeRoot) { this.treeRoot = treeRoot; } }
比较与根谁大,大的往右边,小的往左边:
/** * 动态建立二叉查找树 * * @param treeRoot 根节点 * @param value 节点的值 */ public static void createTree(TreeRoot treeRoot, int value) { //若是树根为空(第一次访问),将第一个值做为根节点 if (treeRoot.getTreeRoot() == null) { TreeNode treeNode = new TreeNode(value); treeRoot.setTreeRoot(treeNode); } else { //当前树根 TreeNode tempRoot = treeRoot.getTreeRoot(); while (tempRoot != null) { //当前值大于根值,往右边走 if (value > tempRoot.getValue()) { //右边没有树根,那就直接插入 if (tempRoot.getRightNode() == null) { tempRoot.setRightNode(new TreeNode(value)); return ; } else { //若是右边有树根,到右边的树根去 tempRoot = tempRoot.getRightNode(); } } else { //左没有树根,那就直接插入 if (tempRoot.getLefTreeNode() == null) { tempRoot.setLefTreeNode(new TreeNode(value)); return; } else { //若是左有树根,到左边的树根去 tempRoot = tempRoot.getLefTreeNode(); } } } } }
测试代码:
int[] arrays = {2, 3, 1, 4, 5}; //动态建立树 TreeRoot root = new TreeRoot(); for (int value : arrays) { createTree(root, value); } //中序遍历树 inTraverseBTree(root.getTreeRoot()); System.out.println("---------------公众号:Java3y"); //先序遍历树 preTraverseBTree(root.getTreeRoot()); System.out.println("---------------公众号:Java3y");
查询树的深度咱们能够这样想:左边的子树和右边的字数比,谁大就返回谁,那么再接上根节点+1就能够了
public static int getHeight(TreeNode treeNode) { if (treeNode == null) { return 0; } else { //左边的子树深度 int left = getHeight(treeNode.getLefTreeNode()); //右边的子树深度 int right = getHeight(treeNode.getRightNode()); int max = left; if (right > max) { max = right; } return max + 1; } }
从上面先序遍历二叉查找树的时候,细心的同窗可能会发现:中序遍历二叉查找树获得的结果是排好顺序的~
那么,若是咱们的二叉树不是二叉查找树,咱们要怎么查询他的最大值呢?
能够这样:
/** * 找出树的最大值 * * @param rootTreeNode */ public static int getMax(TreeNode rootTreeNode) { if (rootTreeNode == null) { return -1; } else { //找出左边的最大值 int left = getMax(rootTreeNode.getLefTreeNode()); //找出右边的最大值 int right = getMax(rootTreeNode.getRightNode()); //与当前根节点比较 int currentRootValue = rootTreeNode.getValue(); //假设左边的最大 int max = left; if (right > max) { max = right; } if (currentRootValue > max) { max = currentRootValue; } return max ; } }
不管是在遍历树、查找深度、查找最大值都用到了递归,递归在非线性的数据结构中是用得很是多的...
树的应用也很是普遍,此篇简单地说明了树的数据结构,高级的东西我也没弄懂,可能之后用到的时候会继续深刻...
乐于输出 干货的Java技术公众号:Java3y。公众号内有200多篇 原创技术文章、海量视频资源、精美脑图,不妨来 关注一下!