上篇博客对链表的结构进行了代码实现,今天让我用java代码带大家了解二叉树的实现原理。
首先大家对二叉树的结构都应该了解,二叉树是由节点构成的,每个节点最多有两个字节点,称为左子节点和右子节点,还有parent父节点。
今天的demo注释写的非常清楚,希望有兴趣的可以跟着注释来看,如果有不懂 的可以在下方评论,我会及时回复。
节点的定义
class Node{
private T date;//节点数据
private Node parent;//父节点
private Node left;//左子节点
private Node right;//右子节点
public Node(T date){
this.date = date;
}
}
二叉树的属性
private Node root;//根节点
数据的添加
/**
* 添加数据
* @param date
* @return
*/
public boolean add(T date){
boolean flag = false;
//判断数据是否已经存在
if(!exist(date)){//数据不存在,可以添加
Node newNode = new Node(date);//构建新节点
//判断是否有数据
if(root==null){
root = newNode;//将新建节点作为根节点
}else{
//找到父节点
Node parent = findParent(date);
if(parent!=null){
if(parent.date.compareTo(date)>0){
parent.left = newNode;//父节点的左节点指向新建节点
}else{
parent.right = newNode;//父节点的右节点指向新建节点
}
newNode.parent = parent;//新建节点的parent节点指向父节点
}
}
flag = true;
}else{
return flag;
}
return flag;
}
删除某个数据
该部分逻辑比较复杂,但我每一种情况都分析到了,按主次情况已经一一列出。
/**
* 删除数据
* @param date
* @return
*/
public boolean remove(T date){
boolean flag = false;
Node node = getNode(date);
if(node!=null){
if(node==root){//1.删除的是根节点
if(node.right==null && node.left==null){//1.1无左右子节点
root = null;
}
else if(node.left==null && node.right!=null){//1.2无左节点
node.right.parent=null;//将要删节点的右侧节点的parent置为null
root = node.right;//将要删节点的右节点作为根节点
}else if(node.left!=null && node.right==null){//1.3无右节点
node.left.parent = null;//将要删节点的左侧节点的parent置为null
root = node.left;//将要删节点的左节点作为根节点
}else{//1.4有左右节点
Node left = split(date);//分割节点,得到要删节点的左节点,
root = left;//将left节点作为根节点
left.parent = null;//将left的parent置为null
}
}else{//2.删除的是非根节点
if(node.right==null && node.left==null){//2.1无左右子节点
if(node.date.compareTo(node.parent.date)<0){//该节点是父节点的左子节点
node.parent.left = null;//将父节点的left置为null
}else{//该节点的父节点的右字节点
node.parent.right = null;//将父节点的right置为null
}
}else if(node.left==null && node.right!=null){//2.2无左节点
node.right.parent = node.parent;//将该节点的右节点的parent指向该节点的parent
node.parent.right = node.right;//将该节点的parent节点的right指向该节点的right节点
}else if(node.left!=null && node.right==null){//2.3无右节点
node.parent.right = node.left;//将该节点的父节点的right指向该节点的left节点
node.left.parent = node.parent;//将该节点得left节点的parent指向该节点的parent节点
}else{//2.4有左右节点
Node left = split(date);//将要删的节点分割,返回该节点的左节点
node.parent.left = node.left;//将要删节点的父节点的left指向该节点的左节点,
left.parent = node.parent;//将分割后的左节点的parent指向要删节点的父节点
}
}
flag = true;
}
return flag;
}
遍历
public void print() {
see(root);
}
/**
* 打印节点数据
* @param root2
*/
private void see(Node node) {//中序打印
if(node!=null){
see(node.left);
System.out.println(node.date);
see(node.right);
}
}
用到的几个辅助方法
- split(T date)—>
将从指定节点切割,并将被切掉节点的右子节点拼接在左节点正确的位置,将剩余的漏出来的左节点返回。
/**
* 分割要删数据的节点,默认左节点作为父节点,并将右节点指向左节点的最大节点的right
* @param date
* @return
*/
private Node split(T date) {
//找到节点
Node node = getNode(date);
//找到左节点的最大数节点
Node big = getBig(node.left);
//将要分割节点的右节点的parent指向big
node.right.parent = big;
//将big的右节点指向node的右节点
big.right = node.right;
return node.left;
}
- getBig(Node node)—>
从指定节点向下寻找,找到数据值最大的节点并返回
/**
* 从node开始找最大的节点
* @param left
* @return
*/
private Node getBig(Node node) {
Node temp = node;
while(temp!=null){
if(temp.right!=null){
temp = temp.right;
}else{
return temp;
}
}
return null;
}
- findParent(T date)—–>
找到新建节点即将插入树中的它的父节点。
/**
* 找到父节点
* @param date
* @return
*/
private Node findParent(T date) {
Node temp = root;//从根节点开始遍历
Node prev = temp;
while(temp!=null){//跳出循环时,temp为null,prev记录上一个节点
prev = temp;//用prev记录上一个节点
if(temp.date.compareTo(date)<0){//从右节点寻找
temp = temp.right;
}else{//从左节点开始遍历
temp = temp.left;
}
}
return prev;
}
- exist(T date)—>
判断树中是否存在该数据
/**
* 判断数据存在与否
* @param date
* @return
*/
private boolean exist(T date) {
boolean flag = false;
Node temp = root;//从根节点开始遍历
while(temp!=null){
if(temp.date.compareTo(date)==0){//数据已经存在
flag = true;
break;
}else{
if(temp.date.compareTo(date)<0){//从右节点寻找
temp = temp.right;
}else{//从左节点开始遍历
temp = temp.left;
}
}
}
return flag;
}
- getNode(T date)—>
在树中找到某个数据对应的节点
/**
* 获得节点
* @param date
* @return
*/
private Node getNode(T date) {
Node temp = root;//从根节点开始遍历
while(temp!=null){
if(temp.date.compareTo(date)==0){//数据已经存在
return temp;
}else{
if(temp.date.compareTo(date)<0){//从右节点寻找
temp = temp.right;
}else{//从左节点开始遍历
temp = temp.left;
}
}
}
return null;
}
因为我不知道如何上传非图片的文件,所以只能把代码帖在最下面了。
package com.it;
/**
*
* 该类实现有序二叉树的数据存储结构
* @author Administrator
*
*/
public class BinaryTree<T extends Comparable<T>> {
class Node{
private T date;//节点数据
private Node parent;//父节点
private Node left;//左子节点
private Node right;//右子节点
public Node(T date){
this.date = date;
}
}
private Node root;//根节点
/**
* 添加数据
* @param date
* @return
*/
public boolean add(T date){
boolean flag = false;
//判断数据是否已经存在
if(!exist(date)){//数据不存在,可以添加
Node newNode = new Node(date);//构建新节点
//判断是否有数据
if(root==null){
root = newNode;//将新建节点作为根节点
}else{
//找到父节点
Node parent = findParent(date);
if(parent!=null){
if(parent.date.compareTo(date)>0){
parent.left = newNode;//父节点的左节点指向新建节点
}else{
parent.right = newNode;//父节点的右节点指向新建节点
}
newNode.parent = parent;//新建节点的parent节点指向父节点
}
}
flag = true;
}else{
return flag;
}
return flag;
}
/**
* 删除数据
* @param date
* @return
*/
public boolean remove(T date){
boolean flag = false;
Node node = getNode(date);
if(node!=null){
if(node==root){//1.删除的是根节点
if(node.right==null && node.left==null){//1.1无左右子节点
root = null;
}
else if(node.left==null && node.right!=null){//1.2无左节点
node.right.parent=null;//将要删节点的右侧节点的parent置为null
root = node.right;//将要删节点的右节点作为根节点
}else if(node.left!=null && node.right==null){//1.3无右节点
node.left.parent = null;//将要删节点的左侧节点的parent置为null
root = node.left;//将要删节点的左节点作为根节点
}else{//1.4有左右节点
Node left = split(date);//分割节点,得到要删节点的左节点,
root = left;//将left节点作为根节点
left.parent = null;//将left的parent置为null
}
}else{//2.删除的是非根节点
if(node.right==null && node.left==null){//2.1无左右子节点
if(node.date.compareTo(node.parent.date)<0){//该节点是父节点的左子节点
node.parent.left = null;//将父节点的left置为null
}else{//该节点的父节点的右字节点
node.parent.right = null;//将父节点的right置为null
}
}else if(node.left==null && node.right!=null){//2.2无左节点
node.right.parent = node.parent;//将该节点的右节点的parent指向该节点的parent
node.parent.right = node.right;//将该节点的parent节点的right指向该节点的right节点
}else if(node.left!=null && node.right==null){//2.3无右节点
node.parent.right = node.left;//将该节点的父节点的right指向该节点的left节点
node.left.parent = node.parent;//将该节点得left节点的parent指向该节点的parent节点
}else{//2.4有左右节点
Node left = split(date);//将要删的节点分割,返回该节点的左节点
node.parent.left = node.left;//将要删节点的父节点的left指向该节点的左节点,
left.parent = node.parent;//将分割后的左节点的parent指向要删节点的父节点
}
}
flag = true;
}
return flag;
}
/**
* 分割要删数据的节点,默认左节点作为父节点,并将右节点指向左节点的最大节点的right
* @param date
* @return
*/
private Node split(T date) {
//找到节点
Node node = getNode(date);
//找到左节点的最大数节点
Node big = getBig(node.left);
//将要分割节点的右节点的parent指向big
node.right.parent = big;
//将big的右节点指向node的右节点
big.right = node.right;
return node.left;
}
/**
* 从node开始找最大的节点
* @param left
* @return
*/
private Node getBig(Node node) {
Node temp = node;
while(temp!=null){
if(temp.right!=null){
temp = temp.right;
}else{
return temp;
}
}
return null;
}
/**
* 找到父节点
* @param date
* @return
*/
private Node findParent(T date) {
Node temp = root;//从根节点开始遍历
Node prev = temp;
while(temp!=null){//跳出循环时,temp为null,prev记录上一个节点
prev = temp;//用prev记录上一个节点
if(temp.date.compareTo(date)<0){//从右节点寻找
temp = temp.right;
}else{//从左节点开始遍历
temp = temp.left;
}
}
return prev;
}
/**
* 判断数据存在与否
* @param date
* @return
*/
private boolean exist(T date) {
boolean flag = false;
Node temp = root;//从根节点开始遍历
while(temp!=null){
if(temp.date.compareTo(date)==0){//数据已经存在
flag = true;
break;
}else{
if(temp.date.compareTo(date)<0){//从右节点寻找
temp = temp.right;
}else{//从左节点开始遍历
temp = temp.left;
}
}
}
return flag;
}
/**
* 获得节点
* @param date
* @return
*/
private Node getNode(T date) {
Node temp = root;//从根节点开始遍历
while(temp!=null){
if(temp.date.compareTo(date)==0){//数据已经存在
return temp;
}else{
if(temp.date.compareTo(date)<0){//从右节点寻找
temp = temp.right;
}else{//从左节点开始遍历
temp = temp.left;
}
}
}
return null;
}
public void print() {
see(root);
}
/**
* 打印节点数据
* @param root2
*/
private void see(Node node) {//中序打印
if(node!=null){
see(node.left);
System.out.println(node.date);
see(node.right);
}
}
}