二叉树——Java初步实现

二叉树图解网址: Visualgo-二叉树
这里是二叉树的图示:
野生的二叉树
不好意思放错了

这一部分就是一棵树,而每一个圆圈则叫做一个节点,每个节点最终指向的内容都是NULL,这里就直接省略了
节点是最基本的单位,那么根据上图,我们应该这样设计节点:
第一部分:指针—— 一个节点会有两个指针,一个指向左边,一个指向右边
第二部分:数据—— 这里的数据是int整型,根据需要我们可以改为其他类,譬如说我在自己写的图书馆管理系统里就改成了Book这一个类
那么为什么要用二叉树作为数据结构呢?我们来看一下二叉树的特点:凡是左边的数都要比右边的小
这一特点可以缩短搜索的时间,每一个节点都只要选择一个方向进行搜索就行了。

下面,我们来具体实现一个二叉树:
一、二叉树的构建与增加节点
首先是节点部分,一个节点应该包括:1.存储的数据2.指向比数据小的节点的指针3.指向比存储数据大的节点的指针
/**
 * @author 起名比做卷子难
 */
public class Node {
	private Node left = null;//指向较小的节点
	private Node Right = null;//指向较大的节点
	private int data;//存储的数据
	public Node(int data) {
		this.data = data;
	}//创建新节点的时候把数据存储上
	public Node getLeft() {
		return left;
	}
	public void setLeft(Node left) {
		this.left = left;
	}
	public Node getRight() {
		return Right;
	}
	public void setRight(Node right) {
		Right = right;
	}
	public int getData() {
		return data;
	}
	public void setData(int data) {
		this.data = data;
	}
}
然后开始构建,实质上就是向二叉树中增添数据,基本思路基于上面所说的二叉树的特点,左边的数据比右边的大,所以从树根开始,小于节点的,去左边,大于节点的,去右边,如果左边(右边)是空,那么就可以创建一个新节点了。
/**
 * @author 起名比做卷子难
 */
public class BinaryTree {
	//这是一棵树
	private Node root = null;//树根
	private Node node = null;//节点指针
}
//增加数据的方法
public void add(int num) {
	//首先判断树根是否为空,若为空新建树根
	if (root == null) {
		root = new Node(num);
		return;
	}
	node = root;//增加每一个数据都要从头开始搜索
	while (true) {
		if (num < node.getData()) {//如果增加的数据比节点数据小
			if (node.getLeft() != null) {//进行判断,也是一个保护,考虑考虑如果节点左边空了呢?
				node = node.getLeft();//节点指针指向左侧节点
				continue;//继续循环
			} else {//节点左边空了
				node.setLeft(new Node(num));//新建节点,同时这个节点的左指针指向新建的节点
				break;//跳出,增加完毕
			}
		} else {//以下的内容为上面的重复,尝试自己进行理解?
			if (node.getRight() != null) {
				node = node.getRight();
				continue;
			} else {
				node.setRight(new Node(num));
				break;
			}
		}
	}
}
现在我们就创建好了一个二叉树了,但是我们看不见,为了进行检验,我们需要对二叉树进行遍历,这里使用DFS(深度优先搜索算法)进行遍历
DFS算法:(如果熟悉可忽略)



DFS其实很好理解,就是先遍历一支,再遍历另一支,就像图中的曲线跟箭头所示,一步步进行遍历,找到null就返回上一步
这里遍历的顺序是:7→4→2→4→6→4→7→11→9→11→15→11→7,看起来优点繁琐,但是实际上十分简单。
我们写一个方法来进行遍历,并在控制台打印以检验我们的构建是否成功
//DFS遍历二叉树
public void dfs(Node nodeBegin) {
	Node node = nodeBegin;//从头开始遍历
	//如果遍历到null了,证明这一支已经遍历完毕,直接返回
	if (node == null) {
		return;
	}
	dfs(node.getLeft());//遍历左支
	dfs(node.getRight());//遍历右支
	System.out.println(node.getData());//在遍历完左右支后再打印这个节点
	return;
}
注意!!!这里有一个问题非常值得探讨,那就是NullPointerException
可能有人会问,既然我们在先前已经定义了一个node作为节点指针,为什么还要在这里多新建一个node呢?
我们不妨试一下,将“Node”删去,得到的运行结果是:
为什么会出现这种情况呢?很简单,在递归的过程中,变量node的值不断改变,直到到达数据为2的这个节点并dfs(2的左支)后,node = null,这时候再进行null.getRight(),当然会报错。这种细节问题最为令人恼火,也最为致命。
当然了,我这里写的很复杂,为的是引出这个问题,如果希望简洁,可以直接不新建Node,把凡是出现“node”的地方换成nodeBegin就可以了
我们再创建一个display()的方法,以后还有用
public void display() {
	dfs(root);
}
既然都写好了,何不运行下?
/**
 * @author 起名比做卷子难
 * 强迫症新建一个类专门用于运行
 */
public class Store {
	public static void main(String[] args) {
		BinaryTree tree = new BinaryTree();//新建一个二叉树
		/*
		 *          7		
		 *         / \
		 *        4   11
		 *       / \  / \
		 *      2  6  9 15
		 *     数据结构图示
		 */
		int[] number = {7,6,11,2,6,9,15};//数组
		for (int num : number){
			tree.add(num);//for each方法向树中添加数据
		}
		tree.display();//运用display方法输出
	}
}
运行结果是: 2 6 4 9 15 11 7(有换行,在这里没有打出来)
如果希望得到的是 2 4 6 7 9 11 15, 只需要把打印的语句放在递归左右支的中间就好。如果希望得到逆序输出的,也只需要调换语句顺序,这里就不解释了。
(这样一来有没有觉得很方便?)
二、二叉树的查找
数据都已经放好了,那么我们就来对二叉树进行查找。查找所基于的也是数据左大右小的特点,譬如说我要找“2”,那么我们先跟根节点7比较,2<7,向左边继续找,2<4,还是向左边找,最后就找到了2。这一步非常简单,原理跟二叉树的遍历相同,而且查找用到的只是while不需要用到递归,更为简单方便。下面是查找的代码。
public Node find(int num) {
	Node node = root;//上面的情况太令人尴尬,索性就新建一个Node确保安全
	while (node != null) {//只要节点不为空
		if (node.getData() > num) {//要查找的小于当前节点
			node = node.getLeft();//向左
			continue;//继续
		} else if (node.getData() < num) {//要查找的大于当前节点
			node = node.getRight();//向右
			continue;//继续
		} else {//要查找的就是当前节点
			return node;//返回当前节点
		}
	}
	return null;//找不到就返回null
}
这里要实现的是找到二叉树的最小值,又或者是某个节点开始的最小值。二叉树找最大值、最小值都非常简单,只要一路向右(向左)就可以了。下面的代码写的是找最小值。
private Node findMin(Node node) {
	while (node.getLeft() != null) {//判断左侧还有没有,起到保护作用
		node = node.getLeft();//一路向左
	}
	return node;//返回的是一整个节点
}
三、二叉树节点的删除
前面做的都非常简单,小儿科而已,下面的删除节点操作就显得有点复杂了。我们先把删除的思路理清。

如图,二叉树的删除分为两部分(这里以4为被删除的节点)
(1)找到要删除的节点,然后在被删除的节点的右节点找一个最小支,然后将被删除节点的左支“嫁接”到最小支的左支,此时的最小支左支肯定为null,否则它就不是最小支。
(2)然后将被删除节点的父节点的左支(也有可能是右支,根据实际情况而定)连到被删除节点的右支(左支)就可以了。

思路其实很简单,难的是实现过程中对于各种情况的讨论,最终代码如下:
private Node get(Node node, int num) {
	if (node == null || node.getData() == num)
		return node;//若已到分支底部或已找到 退出递归
	Node temp = null;//用于存放被删除节点的指针
	Node min = null;//用于存放需要需要删除节点右枝最小节点的指针
	if (node.getData() > num) {//被删除数据小于节点数据,所以下面只要讨论小于的情况
		temp = get(node.getLeft(), num);// 寻找被删除的节点,先递归左边树枝,如果没有找到返回null
		if (temp == null)//没有找到被删除节点
			return null;//没有被删除的节点
		else {//找到了要被删除的节点
			if (temp.getRight() == null){//如果被删除节点没有右支,那么就直接把父节点的左支设为被删除节点的左支。
				node.setLeft(temp.getLeft());//父节点的左支设为被删除节点的左支
				return null;//删除以后就返回null
			}
			min = findMin(temp.getRight());//有右枝就找右枝最小节点
			min.setLeft(temp.getLeft());//右枝的最小节点的左枝设为被删除节点的左枝
			node.setLeft(min);//父节点的左支设为被删除节点有右侧最小节点
			return null;
		}
	} else {
		temp = get(node.getRight(), num);// 寻找被删除的节点,先递归右边树枝,如果没有找到返回null
		if (temp == null)//下面的与上面的代码大同小异,尝试自己进行理解
			return null;
		else {
			if (temp.getRight() == null){
				node.setRight(temp.getLeft());
				return null;
			}//被删除的节点没有右枝
			min = findMin(temp.getRight());
			min.setLeft(temp.getLeft());
			node.setRight(min);
			return null;
		}
	}
}
到此为止,整个二叉树已经实现了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值