Java——二叉树的基础实现

------------------------------------------------------------
  在进行链表结构开发的过程中,会发现所有的数据按照首尾相连的状态进行保存,那么当要进行判断某个数据是否存在时,这种情况下他所面对的时间复杂度为“O(n)”,如果说现在它数据量小,性能上不会有太大的差异,而一旦保存的数据量大了,这个时候时间复杂度就会严重损耗程序的性能。因此,数据结构必须发生改变,应该尽可能的减少检索此树为出发点设计。现在可以利用二叉树来实现。
  如果要想实现一棵树结构的定义,那么就需要去考虑数据的存储形式,在二叉树的实现之中,其基本的实现原理如下:
  1|-取第一个数据为保存的根节点,小于等于根节点的数据要放在节点的左子树,而大于该节点的数据要放在该节点的右子树.
  2|-如果要进行数据检索的话,此时就需要进行每个节点的判断。但是它的判断是区分左右的,所以不会是整个的结构.都进行判断处理,那么它的时间复杂度就是O(logn)
  而对于二叉树而言,在进行数据获取的时候也有三种形式:前序遍历(根-左-右)、中序遍历(左-根-右)、后序遍历(左-右-根)。
  3|-二叉树之中的数据删除操作是非常复杂的,因为在进行数据删除的时候需要考虑的情况是比较多的:
     情况一:如果待删除节点没有子节点,那么直接删掉即可;
     情况二:如果待删除节点只有一个子节点,那么直接删掉,并用其子节点去顶替它;
     情况三:如果待删除节点有两个子节点,这种情况比较复杂:首选找出它的后继节点,然后处理“后继结点"和"被删除节点的父节点"之间的关系,最后处理“后继节点的子节点”和I“被删除节点的子节点”之间的关系。

一.首先写出大体结构

1.存储的数据的类的编写及验证

import java.util.Arrays;

public class Main {
	public static void main(String[] args) {
		Books[] book = new Books[5];  // 定义对象数组
		book[0] = new Books("Java 核心技术:卷1 基础知识",74.8);  // 初始化对象数组
		book[1] = new Books("Java开发实战经典",59.3);
		book[2] = new Books("Effective Java",94.0);
		book[3] = new Books("Spring揭秘",57.8);
		book[4] = new Books("深入理解 Java 虚拟机",62.4);
		Arrays.sort(book);  // 进行排序
		for ( Object b : book ) {  // 输出
			System.out.println(b.toString());
		}
	}
}
class Books implements Comparable<Books> {
    //省略了get以及set
	private String name;  // 属性:书名以及价格
	private double price;
	public Books( String name, double price ) {  // 构造器初始化属性
		this.name = name;
		this.price = price;
	}
	@Override
	public int compareTo(Books o) {  // 价格比较,升序
		if ( this.price - o.price > 0 ) {
			return 1;
		} else if ( this.price - o.price < 0 ) {
			return -1;
		}else {
			return 0;
		}
	}
	@Override  
	public String toString() {  // 覆写toString方法
		return "【书名】:" + this.name + "、【价格】:" + this.price;
	}
}

输出如下:

【书名】:Spring揭秘、【价格】:57.8
【书名】:Java开发实战经典、【价格】:59.3
【书名】:深入理解 Java 虚拟机、【价格】:62.4
【书名】:Java 核心技术:卷1 基础知识、【价格】:74.8
【书名】:Effective Java、【价格】:94.0

2.写二叉树的大致结构

二叉树图
二叉树结构图
总结出其中规律:
  1.左子树.右子树与根节点大小排列顺序为:
    左子树<=根节点<右子树
  2.添加数据时:
    (1).加入的数据先于根节点比较,若大于根节点则往根节点点的右边走,否则,往左边走.例如数字:108
    如图所示

import java.util.Arrays;

public class Main {
	public static void main(String[] args) {
		BinaryTree<Books> tree = new BinaryTree<Books>();
		tree.add(new Books("Java 核心技术:卷1 基础知识",74.8));
		tree.add(new Books("Java开发实战经典",59.3));
		tree.add(new Books("Effective Java",94.0));
		tree.add(new Books("Spring揭秘",57.8));
		tree.add(new Books("深入理解 Java 虚拟机",62.4));
		for ( Object x : tree.toArray() ) {
			System.out.println(x.toString());
		}
		System.out.println(tree.getSize());
	}
}

class Books implements Comparable<Books> {
	private String name;  // 属性:书名以及价格
	private double price;
	public Books( String name, double price ) {  // 构造器初始化属性
		this.name = name;
		this.price = price;
	}
	@Override
	public int compareTo(Books o) {  // 价格比较,升序
		if ( this.price - o.price > 0 ) {
			return 1;
		} else if ( this.price - o.price < 0 ) {
			return -1;
		}else {
			return 0;
		}
	}
	@Override  
	public String toString() {  // 覆写toString方法
		return "【书名】:" + this.name + "、【价格】:" + this.price;
	}
}

class BinaryTree<T extends Comparable<T>> {
	private class Node {
		private Comparable<T> data;  // 存放的数据,注,这个数据得继承了Comparable
		private Node parent;  // 存放父节点
		private Node left;  // 存放左子树
		private Node right;  // 存放右子树
		public Node( Comparable<T> data ) {  // 构造函数初始化数据,没有数据没有意义
			this.data = data;
		}
		/**
		 * 将要保存的数据传入到合适的位置
		 * @param data  要保存的数据
		 */
		public void addNode( Node newNode ) {
			if ( newNode.data.compareTo((T)this.data) <= 0 ) {  // 若所要传入的数据小于等于该节点的数据,则往该节点的左子树传
				if ( this.left ==null ) {  // 该节点的左节点为空,则直接保存数据
					this.left = newNode;
					newNode.parent = this;  // 保存父节点
				} else {  // 否则的话继续向下
					this.left.addNode(newNode);
				}
			}  else {
				if ( this.right == null ) {  // 该节点的右左节点为空,则直接保存数据
					this.right = newNode;
					newNode.parent = this;  // 保存父节点
				} else {  // 有右节点,继续向下
					this.right.addNode(newNode);
				}
			}
		}
		/**
		 * 将数据放入数组
		 */
		public void toArrayNode() {
			if ( this.left != null ) { // 先用方法的是根节点
				this.left.toArrayNode();  // 一直向左子树前进,知道没有下一个左子树,,
			}
			// 表明找到了真棵树的最小值,若没有右子树,则保存数据就是左的保存,开始返回保存根节点,若是有右子树,则对右子树进行递归。
			BinaryTree.this.returnData[BinaryTree.this.foot++] = this.data;  // 开始保存数据
			if ( this.right != null ) {  // 
				this.right.toArrayNode();  // 根据左-根-右,查看这个数据是否有右子树,
			}
		}
		
	}
	//*******************************************
	//************接下来为二叉树功能实现************
	private Node root;  // 根节点
	private int count;  // 计数
	private int foot;  // 脚标
	private Object[] returnData;  // 返回的数据,以数组形式返回
	/**
	 * 
	 * @param data  需要保存的数据
	 * @Exception 数据为空时抛出的异常
	 */
	public void add( Comparable<T> data ) {
		if ( data == null ) {  // 若数据为空则抛出异常
			throw new NullPointerException("数据不能为空!");
		} else {
			Node newNode = new Node(data); // 将传入的数据包装成节点,因为数据本身不具备节点关系匹配的功能
			if ( this.root == null ) {  // 先检测根节点是否为空,若为空则将新节点给根节点
				this.root = newNode;
			} else {  // 根节点不为空
				this.root.addNode(newNode);
			}
			this.count++;
		}
	}
	/**
	 * 
	 * @return  返回一共有多少个数据
	 */
	public int getSize() {
		return this.count;
	}
	/**
	 * 采取的是中序,也就是:左-根-右
	 * @return  以数组形式返回
	 */
	public Object[] toArray() {
		if ( this.root == null ) {  // 若根节点为空,则直接返回空
			return null;
		} else {
			this.returnData = new Object[this.count];  // 创建数组
			this.foot = 0; // 脚标清零
			this.root.toArrayNode();
			return this.returnData;
		}
		
	}
}

输出结果如下:

【书名】:Spring揭秘、【价格】:57.8
【书名】:Java开发实战经典、【价格】:59.3
【书名】:深入理解 Java 虚拟机、【价格】:62.4
【书名】:Java 核心技术:卷1 基础知识、【价格】:74.8
【书名】:Effective Java、【价格】:94.0
5

表明基本的添加数据没问题.排序也正常.

二.加入删除功能

删除的几种情况:
  情况一:如果待删除节点没有子节点,那么直接删掉即可;
  情况二:如果待删除节点只有一个子节点,那么直接删掉,并用其子节点去顶替它;
  情况三:如果待删除节点有两个子节点,这种情况比较复杂:首选找出它的后继节点,然后处理“后继结点"和"被删除节点的父节点"之间的关系,最后处理“后继节点的子节点”和I“被删除节点的子节点”之间的关系。
  注:在操作前应该先找出数据所在的节点.

		public Node getRemoveNode( Comparable<T> data ) {
			if ( data.compareTo((T)this.data) == 0 ) {  // 如果数据相同,则表明找到了
				return this;
			} else if ( data.compareTo((T)this.data) < 0 )  { //  如果数据比这个节点数据小,则往下一个找
				if ( this.left != null ) {  // 下个数据不为空才可以继续往下找,不然直接返回空
					this.left.getRemoveNode(data);
				} else {
					return null;
				}
			} else {  //  数据比这个节点数据大,则往下一个找
				if ( this.right != null ) {  // 下个数据不为空才可以继续往下找,不然直接返回空
					this.right.getRemoveNode(data);
				} else {
					return null;
				}
			}
			return null;
		}

//注:1.以下将"要删除的节点"称之为"删除节点".
  2.对于情况三,左子树与右子树都有,所以需要换的节点是右的最左子树(比如,对应于上图中的120,其最有左子树就是130)

情况一:没有任何的子节点

1.直接将删除结点的父节点设为空.
2.判断删除节点是删除节点父节点的左子树还是右子树,然后将之删除,即设为空.

			if ( removeNode.left == null && removeNode.right == null ) {  // 没有任何子节点
				if ( removeNode.data.compareTo((T)removeNode.parent.left) < 0 ) {  // 先判断删除节点是删除节点的左子树还是右子树,若小于则为左子树
					removeNode.parent = null;
					removeNode.parent.left = null;  // 互相解除关系
				} else {
					removeNode.parent = null;
					removeNode.parent.right = null;  // 互相解除关系
				}
			}

情况二:有一个子节点

  1. 有左子树,但没有右子树
  2. 有右子树,但没有左子树
else if ( removeNode.left != null && removeNode.right == null ) {  // 有左子树,但没有右子树
				if ( removeNode.data.compareTo((T)removeNode.parent.left) <= 0 ) {  // 先判断删除节点是删除节点的左子树还是右子树,若小于则为左子树
					removeNode.parent.left = removeNode.left;
				} else {
					removeNode.parent.right = removeNode.left;  // 互相解除关系
				}
				removeNode.left.parent = removeNode.parent;
} else if ( removeNode.left == null && removeNode.right != null ) {  // 有右子树,但没有左子树
				if ( removeNode.data.compareTo((T)removeNode.parent.left) <= 0 ) {  // 先判断删除节点是删除节点的左子树还是右子树,若小于则为左子树
					removeNode.parent.left = removeNode.right;
				} else {
					removeNode.parent.right = removeNode.right;  // 互相解除关系
				}
				removeNode.right.parent = removeNode.parent;
  }

情况三

1.找到右的最左子树.

else {
				Node moveNode = removeNode.right;  // 设置移动节点
				while ( moveNode.left != null ) {  // 找到右的最左子树.
					moveNode = moveNode.left;
				}
				if ( removeNode.data.compareTo((T)removeNode.parent.left) <= 0 ) {  // 先判断删除节点是删除节点的左子树还是右子树,若小于则为左子树
					removeNode.parent.left = moveNode;
				} else {
					removeNode.parent.right = moveNode;  // 互相解除关系
				}
				moveNode.parent.left = null;
				moveNode.left = removeNode.left;
				moveNode.right = removeNode.right;
				moveNode.parent = removeNode.parent;
				removeNode.left.parent = moveNode;
				removeNode.right.parent = moveNode;
			}
			this.count--;  // 删除了数据,得把数量减小

程序总结:

import java.util.Arrays;


public class Main {
	public static void main(String[] args) {
		BinaryTree<Books> tree = new BinaryTree<Books>();
		tree.add(new Books("Java-100",100));
		tree.add(new Books("Java-60",60));
		tree.add(new Books("Java-140",140));
		tree.add(new Books("Java-50",50));
		tree.add(new Books("Java-70",70));
		tree.add(new Books("Java-120",120));
		tree.add(new Books("Java-160",160));
		tree.add(new Books("Java-40",40));
		tree.add(new Books("Java-80",80));
		tree.add(new Books("Java-110",110));
		tree.add(new Books("Java-130",130));
		tree.add(new Books("Java-105",105));
		tree.add(new Books("Java-125",125));
		tree.add(new Books("Java-135",135));
		tree.add(new Books("Java-123",123));
		tree.add(new Books("Java-128",128));
		tree.add(new Books("Java-137",137));
		tree.add(new Books("Java-139",139));
//		tree.add(new Books("Java-2",2));
//		tree.add(new Books("Java-40",40));
		tree.remove(new Books("Java-100",100));
		for ( Object x : tree.toArray() )
			System.out.println(x.toString());
//			System.out.println(Arrays.toString(tree.toArray()));
	}
}

class Books implements Comparable<Books> {
	private String name;  // 属性:书名以及价格
	private double price;
	public Books( String name, double price ) {  // 构造器初始化属性
		this.name = name;
		this.price = price;
	}
	@Override
	public int compareTo(Books o) {  // 价格比较,升序
		if ( this.price - o.price > 0 ) {
			return 1;
		} else if ( this.price - o.price < 0 ) {
			return -1;
		}else {
			return 0;
		}
	}
	@Override  
	public String toString() {  // 覆写toString方法
		return "【书名】:" + this.name + "、【价格】:" + this.price;
	}
}

class BinaryTree<T extends Comparable<T>> {
	private class Node {
		private Comparable<T> data;  // 存放的数据,注,这个数据得继承了Comparable
		private Node parent;  // 存放父节点
		private Node left;  // 存放左子树
		private Node right;  // 存放右子树
		public Node( Comparable<T> data ) {  // 构造函数初始化数据,没有数据没有意义
			this.data = data;
		}
		/**
		 * 将要保存的数据传入到合适的位置
		 * @param data  要保存的数据
		 */
		public void addNode( Node newNode ) {
			if ( newNode.data.compareTo((T)this.data) <= 0 ) {  // 若所要传入的数据小于等于该节点的数据,则往该节点的左子树传
				if ( this.left ==null ) {  // 该节点的左节点为空,则直接保存数据
					this.left = newNode;
					newNode.parent = this;  // 保存父节点
				} else {  // 否则的话继续向下
					this.left.addNode(newNode);
				}
			}  else {
				if ( this.right == null ) {  // 该节点的右左节点为空,则直接保存数据
					this.right = newNode;
					newNode.parent = this;  // 保存父节点
				} else {  // 有右节点,继续向下
					this.right.addNode(newNode);
				}
			}
		}
		/**
		 * 将数据放入数组
		 */
		public void toArrayNode() {
			if ( this.left != null ) { // 先用方法的是根节点
				this.left.toArrayNode();  // 一直向左子树前进,知道没有下一个左子树,,
			}
			// 表明找到了真棵树的最小值,若没有右子树,则保存数据就是左的保存,开始返回保存根节点,若是有右子树,则对右子树进行递归。
			BinaryTree.this.returnData[BinaryTree.this.foot++] = this.data;  // 开始保存数据
			if ( this.right != null ) {  // 
				this.right.toArrayNode();  // 根据左-根-右,查看这个数据是否有右子树,
			}
		}
		public Node getRemoveNode( Comparable<T> data ) {
			if ( data.compareTo((T)this.data) == 0 ) {  // 如果数据相同,则表明找到了
				return this;
			} else if ( data.compareTo((T)this.data) < 0 )  { //  如果数据比这个节点数据小,则往下一个找
				if ( this.left != null ) {  // 下个数据不为空才可以继续往下找,不然直接返回空
					this.left.getRemoveNode(data);
				} else {
					return null;
				}
			} else {  //  数据比这个节点数据大,则往下一个找
				if ( this.right != null ) {  // 下个数据不为空才可以继续往下找,不然直接返回空
					this.right.getRemoveNode(data);
				} else {
					return null;
				}
			}
			return null;
		}
		
	}
	//*******************************************
	//************接下来为二叉树功能实现************
	private Node root;  // 根节点
	private int count;  // 计数
	private int foot;  // 脚标
	private Object[] returnData;  // 返回的数据,以数组形式返回
	/**
	 * 
	 * @param data  需要保存的数据
	 * @Exception 数据为空时抛出的异常
	 */
	public void add( Comparable<T> data ) {
		if ( data == null ) {  // 若数据为空则抛出异常
			throw new NullPointerException("数据不能为空!");
		} else {
			Node newNode = new Node(data); // 将传入的数据包装成节点,因为数据本身不具备节点关系匹配的功能
			if ( this.root == null ) {  // 先检测根节点是否为空,若为空则将新节点给根节点
				this.root = newNode;
			} else {  // 根节点不为空
				this.root.addNode(newNode);
			}
			this.count++;
		}
	}
	/**
	 * 
	 * @return  返回一共有多少个数据
	 */
	public int getSize() {
		return this.count;
	}
	/**
	 * 采取的是中序,也就是:左-根-右
	 * @return  以数组形式返回
	 */
	public Object[] toArray() {
		if ( this.root == null ) {  // 若根节点为空,则直接返回空
			return null;
		} else {
			this.returnData = new Object[this.count];  // 创建数组
			this.foot = 0; // 脚标清零
			this.root.toArrayNode();
			return this.returnData;
		}
	}
	public void remove( Comparable<T> data ) {
		if ( this.root == null ) {  //根节点为空,直接结束调用
			return ;
		} else if ( this.root.data.compareTo((T)data) == 0 ) {  // 删除的为根节点
			Node moveNode = this.root.right;  // 设置移动节点
			while ( moveNode.left != null ) {  // 找到右的最左子树.
				moveNode = moveNode.left;
			}
			moveNode.left = this.root.left;
			moveNode.right = this.root.right;
			moveNode.parent.left = null;
			this.root = moveNode;
			this.count--;  // 删除了数据,得把数量减小
		} else {
			Node removeNode = this.root.getRemoveNode(data);  // 获取删除的节点
			if ( removeNode !=null ) {  // 表明找到了要删除的节点
				
				if ( removeNode.left == null && removeNode.right == null ) {  // 没有任何子节点
					if ( removeNode.data.compareTo((T)removeNode.parent.left) <= 0 ) {  // 先判断删除节点是删除节点的左子树还是右子树,若小于则为左子树
						removeNode.parent = null;
						removeNode.parent.left = null;  // 互相解除关系
					} else {
						removeNode.parent = null;
						removeNode.parent.right = null;  // 互相解除关系
					}
				} else if ( removeNode.left != null && removeNode.right == null ) {  // 有左子树,但没有右子树
					if ( removeNode.data.compareTo((T)removeNode.parent.left) <= 0 ) {  // 先判断删除节点是删除节点的左子树还是右子树,若小于则为左子树
						removeNode.parent.left = removeNode.left;
					} else {
						removeNode.parent.right = removeNode.left;  // 互相解除关系
					}
					removeNode.left.parent = removeNode.parent;
				} else if ( removeNode.left == null && removeNode.right != null ) {  // 有右子树,但没有左子树
					if ( removeNode.data.compareTo((T)removeNode.parent.left) <= 0 ) {  // 先判断删除节点是删除节点的左子树还是右子树,若小于则为左子树
						removeNode.parent.left = removeNode.right;
					} else {
						removeNode.parent.right = removeNode.right;  // 互相解除关系
					}
					removeNode.right.parent = removeNode.parent;
				} else {
					Node moveNode = removeNode.right;  // 设置移动节点
					while ( moveNode.left != null ) {  // 找到右的最左子树.
						moveNode = moveNode.left;
					}
					if ( removeNode.data.compareTo((T)removeNode.parent.left) <= 0 ) {  // 先判断删除节点是删除节点的左子树还是右子树,若小于则为左子树
						removeNode.parent.left = moveNode;
					} else {
						removeNode.parent.right = moveNode;  // 互相解除关系
					}
					moveNode.parent.left = null;
					moveNode.left = removeNode.left;
					moveNode.right = removeNode.right;
					moveNode.parent = removeNode.parent;
					removeNode.left.parent = moveNode;
					removeNode.right.parent = moveNode;
				}
			this.count--;  // 删除了数据,得把数量减小
			}
		}
	}
}

输出如下:

【书名】:Java-40、【价格】:40.0
【书名】:Java-50、【价格】:50.0
【书名】:Java-60、【价格】:60.0
【书名】:Java-70、【价格】:70.0
【书名】:Java-80、【价格】:80.0
【书名】:Java-105、【价格】:105.0
【书名】:Java-110、【价格】:110.0
【书名】:Java-120、【价格】:120.0
【书名】:Java-123、【价格】:123.0
【书名】:Java-125、【价格】:125.0
【书名】:Java-128、【价格】:128.0
【书名】:Java-130、【价格】:130.0
【书名】:Java-135、【价格】:135.0
【书名】:Java-137、【价格】:137.0
【书名】:Java-139、【价格】:139.0
【书名】:Java-140、【价格】:140.0
【书名】:Java-160、【价格】:160.0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值