数据结构探险篇 四.二叉搜索树的实现

二叉搜索树的意义

二叉搜索树的定义

它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

二叉搜索树的作用
  • 搜索树顾名思义是用来搜索的,现在比如有1000条数据,我让你去查找某一条数据,如果你采用线性遍历的方式去找,假设cpu的查找效率为1ms每条,那你找到这条数据的平均要找1/2,即500条,可能花费时间就是500ms即0.5秒,这个还可以忍受
  • 但现在是大数据时代,数据量动辄上亿,现在我如果让你去40亿条数据中查找某条数据,你还是采用线性遍历的方式的话,你可能就要花费20亿ms即20万秒也就是55个小时,这你还能忍吗,这个时候就该二叉搜索树上场了
  • 在这里插入图片描述
  • 在这十几条数据里我想找到某条数据,自需要根据二叉搜索树的性质,某一结点比我要找的节点大则找它的左子树,否则就找右子树,这样最多自需要4次就可以找到我要的哪条记录
  • 可能这样看也就比线性遍历快几次而已啊,但是二叉树有个性质,当情况最好时,层数为h的二叉树最多可存放2^(h-1)-1个节点,那经过简单的计算,在情况最好时,想要装下40亿条数据需要多少层的二叉树呢,2的32次方为42亿,所以32层的二叉树就可以满足条件,那么我们根据二叉树的性质去搜索某元素的时候,也就最多查找32次就够了
  • 一个是线性遍历,需要遍历20亿条数据,55个小时才能找到数据
  • 一个是二叉树查找,需要找最多32次,0.032秒就够了
  • 这就是数据结构的威力
二叉搜索树的应用

现在而二叉搜索树已经广泛的运用在了搜索领域,例如我们使用的mysql数据库的innerdb引擎就是采用的b+树,也是二叉搜索树的变种
好了话不多说,上代码

数据结构

节点类
package book_1_3;

public class SearchTreeNode <T> {

	SearchTreeNode <T> pTree;//双亲
	SearchTreeNode <T> lChildTree;//左子树
	SearchTreeNode <T> rChildTree;//右子树
	//int deep;//以改节点为根的树的深度
	//int bf;//平衡因子  -1代表左子树深了一层 	+1代表右子树深了一层
	int id;//关键字
	T data;//数据
	
	public SearchTreeNode(int id,T d) {
		pTree = null;
		lChildTree = null;
		rChildTree = null;
		//bf = 0;
		//deep = 1;
		this.id = id;
		data = d;
	}

	public SearchTreeNode<T> getpTree() {
		return pTree;
	}

	public void setpTree(SearchTreeNode<T> pTree) {
		this.pTree = pTree;
	}

	public SearchTreeNode<T> getlChildTree() {
		return lChildTree;
	}

	public void setlChildTree(SearchTreeNode<T> lChildTree) {
		this.lChildTree = lChildTree;
	}

	public SearchTreeNode<T> getrChildTree() {
		return rChildTree;
	}

	public void setrChildTree(SearchTreeNode<T> rChildTree) {
		this.rChildTree = rChildTree;
	}

	public int getBf() {
		return bf;
	}

	public void setBf(int bf) {
		this.bf = bf;
	}

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getDeep() {
		return deep;
	}

	public void setDeep(int deep) {
		this.deep = deep;
	}
	
	
}

二叉搜索树类
public class SearchTree <T> {

	private SearchTreeNode<T> root;//根节点
	private int creaseId;//自增的关键字
	private int len;//当前树的节点数
	private int floor;//当前树的层数
	
	public SearchTree() {
		root = null;
		len = 0;
		floor = 0;
		creaseId = 0;
	}
}

api

向树中添加一个节点
public boolean addNode(T data){
	SearchTreeNode<T> Node = new SearchTreeNode<T>(creaseId++, data);
	return  addNode(Node);
}

public boolean addNode(SearchTreeNode<T> Node){
	//树为空则将此节点赋为根节点
	if(len == 0){
		root = Node;
		len++;
		return true;
	}
	//获取添加的节点的父节点
	SearchTreeNode<T> pNode = getParentNode(Node.getId());
	Node.setpTree(pNode);
	
	//判断此节点是父节点的左子树还是右子树
	if(pNode.getId() > Node.getId()){
		//父节点的左子树
		pNode.lChildTree = Node;
		pNode.setBf(pNode.getBf() - 1);
	}else{
		//父节点的右子树
		pNode.rChildTree = Node;
		pNode.setBf(pNode.getBf() + 1);
	}
	
	//更新节点数与层数
	len++;
	//floor = (int)(Math.log(len)/Math.log(2)) + 1;
	return true;
}
前遍历某树
public void preTraver(SearchTreeNode<T> Node){
	if(Node != null){
		System.out.print(Node.getData());
	}
	
	if(Node.getlChildTree() != null){
		preTraver(Node.getlChildTree());
	}
	
	if(Node.getrChildTree() != null){
		preTraver(Node.getrChildTree());
	}
}
中序遍历树
public void midTraver(SearchTreeNode<T> Node){
	if(Node.getlChildTree() != null){
		midTraver(Node.getlChildTree());
	}
	
	if(Node != null){
		System.out.print(Node.getData());
	}
	
	if(Node.getrChildTree() != null){
		midTraver(Node.getrChildTree());
	}
}
后序遍历树
public void afterTraver(SearchTreeNode<T> Node){
	if(Node != null){
		//System.out.println("now :"+Node.getData());
		if(Node.getlChildTree() != null){
			afterTraver(Node.getlChildTree());
		}
		
		if(Node.getrChildTree() != null){
			afterTraver(Node.getrChildTree());
		}
		
		System.out.print(Node.getData());
	}
}
根据关键字查找某一个节点
public SearchTreeNode<T> getNode(int id){
	//根据完全二叉树节点有序的性质去查找某一结点
	//1.从底向上查找,从根节点到目标节点的访问路径
	int nowId = root.getId();
	//判断当前查找的节点是否为根节点
	if(nowId == id){
		return root;
	}
	
	SearchTreeNode<T> temp = root;
	while(nowId != id){
		//判断是左节点还是右节点
		if(id > nowId){
			temp = temp.rChildTree;
		}else{
			temp = temp.lChildTree;
		}
		
		if(temp == null){
			System.out.println("该节点未找到");
			return null;
		}
		
		nowId = temp.getId();
	}
	
	return temp;
}
根据关键字给想插入的某新一结点,查找符合条件的父节点
public SearchTreeNode<T> getParentNode(int id){
	SearchTreeNode<T> temp = root;
	
	
	//树判空
	if(len <= 0){
		return null;
	}
	
	
	
	while(true){
		if(temp.getId() > id){
			//比父节点小,则去看父节点的左节点是否为空
			//为空则正好插入,该节点为目标节点
			if(temp.getlChildTree() == null){
				return temp;
			}else{
				temp = temp.getlChildTree();
			}
		}else{
			//比父节点大,则去看父节点的右节点是否为空
			//为空则正好插入,该节点为目标节点
			if(temp.getrChildTree() == null){
				return temp;
			}else{
				temp = temp.getrChildTree();
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值