二叉树Java

目录

创建二叉树

实现思路

代码展示

代码讲解

遍历二叉树

实现思路

代码展示

代码讲解

总结


创建二叉树

实现思路

想要创建二叉树,首先就要有一个理论基础。

二叉树(binary tree)可以理解为子节点最多为2的树,也就是说每个节点(node)度(degree)都不大于2。什么是度呢?度指的是一个节点拥有子树的数目。如下图,A点的度为2,因为它有两个节点B,C,同时,A也被称为根节点,因为该节点上面再没有点了。另外,我们可以看到,这棵二叉树有4层节点,换句话说,该树的深度为4。

那节点包含什么呢?节点包含了数据项以及指向子树节点的指针。我们应该注意到了H,I,L等在最后一层的节点,它们没有子节点。像这种度为0的节点,我们称之为叶节点(leaf)

二叉树没有规定每个节点的度必须是二,于是这里就衍生出了几种二叉树。

 

满二叉树

第一种是满二叉树。如上图所示,满二叉树指的是度只有0或2的树,而且度为0的节点在同一层内。其实就是除了最后一层没有任何子节点外,每一层的节点都有两个子节点。那这满二叉树的总节点又是多少呢?让我们观察一下。深度为2的满二叉树总节点为3;深度为3的总节点是7;深度为4的总节点是15……想必大家都已经发现规律了,对于一个深度为h的满二叉树,其总节点数=2^h-1。由此我们也不难推出,满二叉树中每一层的节点数=2^(h-1)。

完全二叉树

 第二种是完全二叉树。上图(图2)就是一个完全二叉树。完全二叉树由满二叉树衍生而来。若一个二叉树有h层,从1~h-1层的所有节点数都达到最大个数,且第 h 层所有的结点都连续集中在最左边,就是完全二叉树。比如回到图1,我们砍掉O点后它就变成了个完全二叉树。什么?还想砍?我们需要按照这个顺序砍:O->N->M……如果你砍完O后就心急火燎的把M砍掉了,这就不是一个完全二叉树了,因为M比N更靠左,要先砍N才能砍M。

退化二叉树

 第三种是退化二叉树。如上图,退化二叉树十分显著的特征就是:1.该树的度只有0或1;  2.要不所有节点都处于左侧,要不所有节点都处于右侧;  3.长相与链表神似。

平衡二叉树

注:上图来源于知乎文章:什么是平衡二叉树(AVL)

平衡二叉树(AVL)。上图就是一个平衡二叉树(图内的数字是每个节点内储存的数据)。平衡二叉树的左子树和右子树的高度之差的绝对值不超过1且它的左子树和右子树都是一颗平衡二叉树。

搜索二叉树 - 斐波那契数列

二叉搜查树。上述树的类型针对的是树的整体结构,而二叉搜查树针对的是二叉树节点的值的分配。在二叉搜查树中,每一个节点的左子节点都小于等于该节点的值,而右子节点大于该节点的值。搜查树,顾名思义,方便搜查。如果大家了解过二分查找(binary search)和线性查找( linear search),就会发现,这里二叉搜查树对值的分配就很符合二分查找的原理,而退化二叉树就对应这线性查找。毫无疑问,二叉搜查树在支持搜索节点值这方面是极具优越性的。我们一会初始化的也是二叉搜查树。

二叉树:数组/列表储存 - 节点对应索引

二叉树的储存形式。二叉树有两种储存方式。我们代码演示的是链表储存,实现起来相对直接且简单。其实数组储存也是比较主流的一种储存方式。大家可能会比较疑惑,毕竟一个节点只包含指针和数据项,怎么转换成有索引的数组元素呢?我们可以给每个节点标上索引。如果节点的索引是i,左子树就是i*2+1,右子树则是i*2+2。听起来比较抽象,我们看上图理解一下:(节点处标的是索引)


 

代码展示

package advance.dataStructure;

class TreeNode {
	int val;
	TreeNode left;
	TreeNode right;

	TreeNode() {
	}
	TreeNode(int val) {//overload
		this.val = val;
	}

	TreeNode(int val, TreeNode left, TreeNode right) {
		this.val = val;
		this.left = left;
		this.right = right;
	}
};
//test
public class Main{
	public static void main(String[] args) {
		TreeNode node=new TreeNode(4);
		System.out.println(node.left);
	}
}
package advance.dataStructure;

import java.util.ArrayList;
import java.util.List;
import advance.algorithm.sortArray.Select;

public class BinaryTree_init {
	public TreeNode root = null;

	public BinaryTree_init(int data) {
		this.root = new TreeNode(data);
	}

	public void addVal(int data) {//balanced binary tree
		TreeNode node = new TreeNode(data);
		TreeNode temp = this.root;
		while (true) {
			if (temp.val >= data) {
				if (temp.left != null) {
					temp = temp.left;
				} else {
					temp.left = node;
					break;
				}

			} else {
				if (temp.right != null) {
					temp = temp.right;
				} else {
					temp.right = node;
					break;
				}
			}
		}
	}

	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();//创建列表对象
		int[] arr = { 1, 5, 9, 7, 4, 2, 3 };
		arr = Select.select(arr);

        //将数组里的元素排序好后全部添加到列表中来
		for (int e : arr)
			list.add(e);
		int mid = Math.round(arr.length / 2);
		list.remove(mid);//删除中位数,因为该数要作根节点的值

        //初始化二叉树并赋值
		BinaryTree_init bt = new BinaryTree_init(arr[mid]);
		for (int e : list)
			bt.addVal(e);

        //遍历二叉树
		ErgordicBinaryTree obj = new ErgordicBinaryTree();
		List<Integer> lst = obj.order(bt.root, 1);
		for (int e : lst)
			System.out.println(e);

	}
}

代码讲解

treeNode这个语法和链表的listNode十分相似,除了treeNode有两个“next”,其他性质和listNode都差不多。另外,addVal()用的是链式存储,前序遍历。得到的树还是一个二叉树。


遍历二叉树

实现思路

遍历二叉树有三种主要实现思路,其中两种是迭代法。我们今天讲的思路是递归法,是这三种方法中最简单也最容易理解的。遍历有分为前序遍历,中序遍历和后序遍历。这三者有什么不同呢?我们可以看下图理解。(图中标的是每种遍历取节点数据项的顺序) 

前序遍历

前序遍历大家看了上图理解的估计也七七八八了,我们发现,前序遍历的顺序刚好就是平衡二叉树从小到大的节点值。

中序遍历

中序遍历 

 

后序遍历

后序遍历在处理逆波兰表达式上很有效果。如果把第三层的节点值算作数,第二层节点算作加减而根节点为乘除的话,按照这个顺序遍历倒也是很方便 


 代码展示

package advance.dataStructure;

import java.util.ArrayList;
import java.util.List;

public class ErgordicBinaryTree {
	public List<Integer> order(TreeNode root, int num) {
		List<Integer> bt = new ArrayList<>();
		switch (num) {
		case 0:
			preorder(root, bt);
			break;
		case 1:
			inorder(root, bt);
			break;
		case 2:
			postorder(root, bt);
			break;
		default:
			break;
		}
		return bt;
	}

	private void preorder(TreeNode root, List<Integer> R) {
		if (root == null) {
			return;
		}
		R.add(root.val);
		preorder(root.left, R);
		preorder(root.right, R);
	}

	private void inorder(TreeNode root, List<Integer> R) {
		if (root == null) {
			return;
		}
		inorder(root.left, R);
		R.add(root.val);
		inorder(root.right, R);
	}

	private void postorder(TreeNode root, List<Integer> R) {
		if (root == null) {
			return;
		}
		postorder(root.left, R);
		postorder(root.right, R);
		R.add(root.val);
	}
}

代码讲解

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340