数据结构树之二叉树

  1. 为什么需要二叉排序树:
    对于数组,查找的效率较高,但是删除和添加的效率较低;而对于链表,查找的效率较低,删除和增加的效率较高。那么有没有一种方法使得各种效率都较高呢?使用二叉树的存储结构可以做到
  2. java中表示树
    创建一个名为Node的类,表示数的节点
class Node<T>{
	//数据
	T data;
	//左孩子
	Node<T> leftChild;
	//右孩子
	Node<T> rightChild;
	
	//构造方法
	public Node(T data)
	{
		this.data=data;
		//这两部实际上在类加载的时候已经初始化为null
		leftChild=null;
		rightChild=null;
	}

}

声明为泛型类的目的是考虑到树存储的数据可以由使用者来确定,但是这里要求数据对象必须实现了Comparable接口并重写了compareTo()方法,这样才能比较对象的大小

//这里只是前面的代码
public class BinaryTree<T extends Comparable<T>>{
	//树的根root
	Node<T> root;
}
  1. 节点的插入(也就是二叉树的构建过程)
    这里假设插入的节点为node,里面
    (1)如果根节点不存在,那么先建立根节点
    (2)否则,令current=root,判断node的数据对象和current数据对象的大小,如果小于,则判断current的左子树是否为空,如果为空则把node节点插入current的左子树,否则current=current.leftChild;如果大于,则判断current的右子树是否为空,如果为空则把node节点插入current的右子树,否则current=current.rightChild
	public void insert(Node<T> node)
	{
		//如果root为空,先插入root,否则根据大小插入子树中
		if(root==null)
			root=node;
		else
		{
			Node<T> current=root;
			//node比root小
			while(true)
			{
				if(node.data.compareTo(root.data)<0)
				{
					//如果左节点已经为空,那么插入到这来,如果不是空,那就是继续比较
					if(current.leftChild==null)
					{
						current.leftChild=node;
						return;
					}
					current=current.leftChild;
				}//node大于等于root,查找的时候是返回第一个找到的节点
				else
				{
					if(current.rightChild==null)
					{
						current.rightChild=node;
						return;
					}
					current=current.rightChild;
				}
			}
		}
	}
  1. 节点的查找
    (1)如果根节点为空,那么return null;表示查找失败
    (2)如果根节点不为空,令current=root,然后开始判断node的数据对象和current数据对象是否相等;如果相等,则return current;如果不相等,则判断两者大小,然后根据大小来决定查找左子树还是右子树。在这个过程中,如果需要查找的左子树或者右子树为空,那么return null表示找不到。
	public Node<T> select(Node<T> node)
	{
		if(root==null)
			return null;
		else
		{
			Node<T> current=root;
			while(node.data.compareTo(current.data)!=0)
			{
				if(node.data.compareTo(current.data)<0)
				{
					if(current.leftChild==null)
						return null;
					current=current.leftChild;
				}
				if(node.data.compareTo(current.data)>0)
				{
					if(current.rightChild==null)
						return null;
					current=current.rightChild;
				}
			}
			return current;
		}
	}
	
  1. 节点的删除
    这里设要删除的节点为node,要删除的节点的父节点为parent
    这个部分是最复杂的,这里首先需要看node具有的子节点个数
    (1)如果没有子节点,那么直接parent指向node的位置赋null,表示删除掉了node
    (2)如果node有一个子节点,那么只需要把parent指向node的位置指向node这个子节点解可以了
    (3)如果node有2个子节点,这种情况是最复杂的,这里把last声明为node节点安装中序遍历的后继节点,lastParent是last的父节点
    在这里插入图片描述
    这个表示要被删除的节点node的右节点没有左节点,那么上图的last就是node的后继节点,所以只需要把last设置到node的位置就能完成删除node的目的(parent.XXX=last,last.leftChild=node.leftChild)

!
这个表示node的后继节点不是node的右节点,因为last是node中序遍历的后继节点,所以last的左子节点是null的,但是last右子节点不一定为空,所以需要先把last.rightChild赋值给parent.leftChild,然后修改last为node,然后把parent指向last,这样就能完成删除node的任务

(4)这里有一种特殊的节点,也就是root根节点,因为一开始current=root,parent=null,所以需要对根节点做特殊处理,否则删除根节点将会尝试空指针异常
下面是源码,因为没有提取相似的代码所有有点长

	public boolean delete(Node<T> node)
	{
		if(root==null||node==null)
		{
			return false;
		}
		else {
			//要删除的节点
			Node<T> current=root;
			//记录这个节点的父节点,root节点的父节点我null
			Node<T> parent=null;
			//记录current节点所在的方向
			Direct direct=Direct.left;
			
			//找到这个节点和他的父节点
			while(node.data.compareTo(current.data)!=0)
			{
				System.out.println("node.data"+node.data);
				System.out.println("current.data"+current.data);
				parent=current;
				if(node.data.compareTo(current.data)<0)
				{
					current=current.leftChild;
					direct=Direct.left;
				}
				else
				{
					current=current.rightChild;
					direct=Direct.right;
				}
				//左子树和右子树都为空则表明找不到这个要删除的节点,return false
				if(current==null)
					return false;
			}
			
			//删除的节点左右子树都为空,那么直接把父节点的Direct方向设置null即可
			if(current.leftChild==null&&current.rightChild==null)
			{
				//如果删除打的是根节点
				if(current==root)
				{
					root=null;
				}
				else if(direct==Direct.left)
					parent.leftChild=null;
				else
					parent.rightChild=null;
				return true;
			}
			//要删除的节点当且仅有一个子树
			//右子树不空,那么把父节点的Direct方向设为其右子树即可
			if(current.leftChild==null)
			{
				//如果删除打的是根节点
				if(current==root)
				{
					root=current.rightChild;
				}
				else if(direct==Direct.left)
					parent.leftChild=current.rightChild;
				else
					parent.rightChild=current.rightChild;
				return true;
			}
			//左子树不空,那么把父节点的Direct方向设为其左子树即可
			if(current.rightChild==null)
			{
				//如果删除打的是根节点
				if(current==root)
				{
					root=current.leftChild;
				}
				else if(direct==Direct.left)
					parent.leftChild=current.leftChild;
				else
					parent.rightChild=current.leftChild;
				return true;
			}
			//左右子树都不为空的删除情况
			//过程是把要删除节点的先序下个节点A放在删除节点的位置
			//如果A是要删除节点的右节点,那么直接把parent Direct方向的子节点赋为A即可
			//如果A不是要删除节点的右节点,除了parent Direct需要设置,A的右子树设置为删除节点的右子树
			
			//表示A的父节点
			Node<T> lastParent=current;
			//A
			Node<T> last=current.rightChild;
			//A的左节点为空,也就是A就是要删除节点的下个节点
			if(last.leftChild==null)
			{
				//如果删除的是根节点
				if(current==root)
				{
					root=last;
					root.leftChild=current.leftChild;
				}//把parent的Direct方向指向last,也就是A
				else if(direct==Direct.left)
					parent.leftChild=last;
				else
					parent.rightChild=last;
				
				//A的左子树指为要删除节点的左子树
				last.leftChild=current.leftChild;
			}//A的左节点不为空,那么要删除节点的下个节点是A的最左左节点,然后就是新的A
			else
			{
				//找到这个下个节点
				while(last.leftChild!=null)
				{
					lastParent=last;
					last=last.leftChild;
				}
				
				
				//把的A的父节点的左子树设为A的右子树
				lastParent.leftChild=last.rightChild;
				
				//A的左子树指为要删除节点的左子树
				last.leftChild=current.leftChild;
				
				//A的右子树指向要删除节点的右子树
				last.rightChild=current.rightChild;
				
				//如果是根节点
				if(current==root)
				{
					root=last;
				}//把parent的Direct方向指向last,也就是A
				else if(direct==Direct.left)
					parent.leftChild=last;
				else
					parent.rightChild=last;
			}
			return true;
		}
	}
	

7.节点的遍历(前序,中序,后序)
这个就是使用递归来做,这里会传入一个实现了List 接口的对象过程,然后把遍历的数据封装到List里面(判断使用哪个方法遍历是根据枚举来判断的)

	public void traverse(Traverse traverse,List<T> list)
	{
		switch (traverse) {
		case preorder:preorder(root,list); break;
		case inorder:inorder(root,list); break;
		case postorder:postorder(root,list);break;
		default:
			break;
		}
	}
	
	/**
	 * 前序遍历
	 * @param current
	 */
	public void preorder(Node<T> current,List<T> list)
	{
		if(current!=null)
		{
			list.add(current.data);
			preorder(current.leftChild,list);
			preorder(current.rightChild,list);
		}
	}
	
	/**
	 * 中序遍历
	 * @param current
	 */
	public void inorder(Node<T> current,List<T> list)
	{
		if(current!=null)
		{
			preorder(current.leftChild,list);
			list.add(current.data);
			preorder(current.rightChild,list);
		}
	}
	
	/**
	 * 后序遍历
	 * @param current
	 */
	public void postorder(Node<T> current,List<T> list)
	{
		if(current!=null)
		{
			preorder(current.leftChild,list);
			preorder(current.rightChild,list);
			list.add(current.data);
		}
	}
	
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值