【算法与数据结构】二叉搜索树的Java实现
为了更加深入了解二叉搜索树,博主自己用Java写了个二叉搜索树,有兴趣的同学可以一起探讨探讨。
首先,二叉搜索树是啥?它有什么用呢?
二叉搜索树, 也称二叉排序树,它的每个节点的数据结构为1个父节点指针,1个左孩子指针,1个有孩子指针,还有就是自己的数据部分了,因为只有左右两孩子,所以才叫二叉树,在此基础上,该二叉树还满足另外一个条件:每个结点的左孩子都不大于该结点&&每个结点的右孩子都大于该结点。这样,我们队这棵树进行中序遍历,就能把key从小到大排序了……
那么问题来了,我都有线性表有链表了,我还要它干啥?两个字!效率!
相比线性表,你要搜索一个key,就要执行一次线性时间,算法复杂度为O(n);而用二叉搜索树,算法效率是O(lgn)!这是很诱人的数字。下面我用Java实现以下二叉搜索树,你自然就明白为什么算法复杂度是O(lgn)了。
其次,写一个数据结构,自然而然也要实现对这个数据结构的增、删、查、改了。
下面是我的思路:
- 创建树:我是通过一个一个结点的插入来建立一棵二叉搜索树。
- 搜索结点:从根节点开始,进行key的比较,小了就往左走,大了就往右走,最后到了叶子结点都还没有的话,那么该树就不存在要搜索的结点了。
- 修改结点:修改其实就是查询,在查询之后把结点的数据部分给改了而已,这里我就不重复去实现了。
- 删除结点:这个应该就是最难的了,所以我有必要详细讲,先上图(不好意思,懒得用软件画图了,将就将就下哈):
- 此结点是叶子结点,这个最简单啦,直接把结点给释放掉就行了。(如图删除9)
- 此结点只有左孩子,这个也简单啦,直接把左子树替换过来就行了。(如图删除3)
- 此结点只有右孩子,同上。(如图删除8)
- 此结点有左右孩子,当出现这种情况时(如图删除7),我们就要找出该结点的后继结点(因为右子树肯定存在,所以找肯定在右子树中),然后把这个后继结点替换到要删除的结点中,然后继续执行对这个后继结点的删除操作(递归删除操作就行了)。
- 当该结点有右孩子时,后继结点就在右子树中,就是该右子树的最小结点
- 当该结点没有右孩子时,那后继结点就满足这个条件:该后继结点是该结点的祖先&&该结点位于该结点的左子树中(如图中的9的后继结点是12)
public class Test {
public static void main(String[] args) {
int[] datas={12,4,5,7,4,8,3,2,6,9};
BinTree tree=new BinTree(datas);
tree.preOrderTraverse();//先序遍历
tree.midOrderTraverse();//中序遍历
tree.postOrderTraverse();//后序遍历
tree.insert(15); //插入结点
tree.search(7); //查询结点
tree.search(100); //查询一个不存在的结点
tree.getMax(); //获取最大值
tree.getMin(); //获取最小值
tree.getPre(7); //前驱结点
tree.getPre(2); //最前的前驱结点
tree.getPost(7); //后继结点
tree.getPost(15); //最后的后继结点
tree.delete(5); //删除结点
tree.delete(0); //删除一个不存在的结点
}
}
public class BinTree {
Node root=null;
private class Node{
Node parent=null;
Node leftChild=null;
Node rightChild=null;
int key;
public Node(int data) {
this.key=data;
}
}
public BinTree(int[] datas) {
buildTree(datas);
}
private void buildTree(int[] datas) {
for (int i = 0; i < datas.length; i++) {
Node node=new Node(datas[i]);
insertNode(node);
}
}
private void insertNode(Node node) { //插入结点
Node next=this.root;
Node cur=null; //用来保存当前结点
while(next!=null){ //当到达叶子结点时,确认位置!
cur=next;
if(node.key>=cur.key){
next=next.rightChild;
}else{
next=next.leftChild;
}
}
node.parent=cur; //插入该结点!
if(cur==null){
this.root=node; //该树为空树,所以这个是根节点
}else if(node.key>=cur.key){
cur.rightChild=node;
}else{
cur.leftChild=node;
}
}
/*
* 插入一个数
*/
public void insert(int data){
Node node=new Node(data);
System.out.println("插入结点:"+data);
insertNode(node);
this.midOrderTraverse();
} /*
* 先序遍历
*/
public void preOrderTraverse(){
System.out.println("先序遍历:");
preOrderTraverse(root);
System.out.println();
}
private void preOrderTraverse(Node node){ //先序遍历
if(node!=null){
System.out.print("-"+node.key+"-");
preOrderTraverse(node.leftChild);
preOrderTraverse(node.rightChild);
}
}
/*
* 中序遍历
*/
public void midOrderTraverse(){
System.out.println("中序遍历:");
midOrderTraverse(root);
System.out.println();
}
private void midOrderTraverse(Node node){ //中序遍历
if(node!=null){
midOrderTraverse(node.leftChild);
System.out.print("-"+node.key+"-");
midOrderTraverse(node.rightChild);
} } /*
* 后序遍历
*/
public void postOrderTraverse(){
System.out.println("后序遍历:");
postOrderTraverse(root);
System.out.println();
}
private void postOrderTraverse(Node node){ //后序遍历
if(node!=null){
System.out.print("-"+node.key+"-");
postOrderTraverse(node.leftChild);
postOrderTraverse(node.rightChild);
}
} /*
* 搜索结点
*/
public void search(int data){
System.out.println("您要查找的是:"+data);
Node node;
if((node=searchNode(new Node(data)))==null){
System.out.println("树中没有该结点!");
}else{
System.out.println("查找"+node.key+"成功!");
}
} private Node searchNode(Node node){ //private供内部调用,搜索结点
if(node==null){
System.out.println("输入为空,查找失败!");
}else{
if(root==null){
System.out.println("该树为空树!");
}else{ //开始查找
boolean isFound=false;
Node x=root;
Node y=null;
while(!isFound&&x!=null){ //当查到或者到了叶子节点还没查到时,终结!
y=x;
if(node.key==x.key){
isFound=true;
}else{ //通过比较大小往下面查找
if(node.key>x.key){
x=x.rightChild;
}else{
x=x.leftChild;
}
}
}
if(isFound){ //没找到的话,在最后返回null
return y;
}
}
}
return null;
} /*
* 获取最大值
*/
public void getMax(){
Node node;
if((node=getMaxNode(root))==null){
System.out.println("该树为空!");
}else{
System.out.println("最大的结点是:"+node.key);
} } private Node getMaxNode(Node node){ //获取最大值
if(node!=null){
Node x=node;
Node y=null;
while(x!=null){ //一直往右遍历直到底就是最大值了!
y=x;
x=x.rightChild;
}
return y;
}
return null;
} /*
* 获取最小值
*/
public void getMin(){
Node node;
if((node=getMinNode(root))==null){
System.out.println("该树为空!");
}else{
System.out.println("最小的结点是:"+node.key);
}
}
private Node getMinNode(Node node){ //获取最小值
if(node!=null){
Node x=node;
Node y=null;
while(x!=null){ //一直往左遍历直到底就是最小值了!
y=x;
x=x.leftChild;
}
return y;
}
return null;
} /*
* 获取前驱结点
*/
public void getPre(int data){
Node node=null;
System.out.println(data+"的前驱结点:");
if((node=getPreNode(searchNode(new Node(data))))==null){
System.out.println("该结点不存在或无前驱结点!");
}else{
System.out.println(data+"的前驱结点为:"+node.key);
}
} private Node getPreNode(Node node){ //获取前驱结点
if(node==null){
return null;
}
if(node.leftChild!=null){ //当有左孩子时,前驱结点就是左子树的最大值
return getMaxNode(node.leftChild);
}else{//当不存在左孩子时,前驱结点就是——它的祖先,而且,它在这个祖先的右子树中。这句话自己画图就能理解了
Node x=node;
Node y=node.parent;
while(y!=null&&x==y.leftChild){
x=y;
y=y.parent;
}
return y;
}
} /*
* 获取后继结点
*/
public void getPost(int data){
Node node=null;
System.out.println(data+"的后继结点:");
if((node=getPostNode(searchNode(new Node(data))))==null){
System.out.println("该结点不存在或无后继结点!");
}else{
System.out.println(data+"的后继结点为:"+node.key);
}
} private Node getPostNode(Node node){ //获取后继结点
if(node==null){
return null;
}
if(node.rightChild!=null){ //当有右孩子时,前驱结点就是右子树的最小值
return getMinNode(node.rightChild);
}else{//当不存在右孩子时,后继结点就是——它的祖先,而且,它在这个祖先的左子树中。这句话自己画图就能理解了
Node x=node;
Node y=node.parent;
while(y!=null&&x==y.rightChild){
x=y;
y=y.parent;
}
return y;
}
} /*
* 删除结点
*/
public void delete(int data){
Node node;
if((node=searchNode(new Node(data)))==null){//注意!这里不能new结点!你必须从树中找该结点!new就是初始化了
System.out.println("二叉树中不存在此结点!");
return;
}
deleteNode(node);
System.out.println("删除结点"+data+"后:");
this.midOrderTraverse();
} private void deleteNode(Node node){
if(node==null){
System.out.println("删除结点不能为空!");
return;
}
replacedNode(node);
} private void replacedNode(Node node) { //替换结点
if(node.leftChild!=null
&&node.rightChild!=null){ //当有左右孩子时,用后继结点替换
replacedNodeOfPost(node);
}
else
{
if(node.leftChild!=null){ //当只有左孩子时,直接用左子树替换
node=node.leftChild;
}else if(node.rightChild!=null){ //只有右孩子时,直接有子树替换
node=node.rightChild;
}else{ //当没有左右孩子时,就直接释放了这个结点
freeNode(node);
}
}
} private void freeNode(Node node) { //释放该结点,断掉其与父结点的链接
if(node==node.parent.leftChild){
node.parent.leftChild=null;
}else{
node.parent.rightChild=null;
}
} private void replacedNodeOfPost(Node node) {
Node y=this.getPostNode(node); //找后继结点
node.key=y.key;
replacedNode(y); //替换了key之后,再一次递归把现在这个结点给替换了!
} }
最后是测试结果:
-12--4--3--2--5--4--7--6--8--9-
中序遍历:
-2--3--4--4--5--6--7--8--9--12-
后序遍历:
-12--4--3--2--5--4--7--6--8--9-
插入结点:15
中序遍历:
-2--3--4--4--5--6--7--8--9--12--15-
您要查找的是:7
查找7成功!
您要查找的是:100
树中没有该结点!
最大的结点是:15
最小的结点是:2
7的前驱结点:
7的前驱结点为:6
2的前驱结点:
该结点不存在或无前驱结点!
7的后继结点:
7的后继结点为:8
15的后继结点:
该结点不存在或无后继结点!
删除结点5后:
中序遍历:
-2--3--4--4--6--7--8--9--12--15-
二叉树中不存在此结点!
【算法与数据结构】二叉搜索树的Java实现的更多相关文章
- Java与算法之(13) - 二叉搜索树
查找是指在一批记录中找出满足指定条件的某一记录的过程,例如在数组{ 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }中查找数字15,实现代码很简单 ...
- Java数据结构——二叉搜索树
定义二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若 ...
- 数据结构-二叉搜索树和二叉树排序算法(python实现)
今天我们要介绍的是一种特殊的二叉树--二叉搜索树,同时我们也会讲到一种排序算法--二叉树排序算法.这两者之间有什么联系呢,我们一起来看一下吧. 开始之前呢,我们先来介绍一下如何创建一颗二叉搜索树. 假 ...
- 二叉搜索树 思想 JAVA实现
二叉搜索树:一棵二叉搜索树是以一棵二叉树来组织的,这样一棵树可以使用链表的数据结构来表示(也可以采用数组来实现).除了key和可能带有的其他数据外,每个节点还包含Left,Right,Parent,它 ...
- 数据结构-二叉搜索树(BST binary search tree)
本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 二叉搜索树简介 顾名思义,二叉搜索树是以一棵二叉树来组织的,这样的一棵树可以用一个链表数据结构来 ...
- 数据结构-二叉搜索树的js实现
一.树的相关概念 1.基本概念 子树 一个子树由一个节点和它的后代构成. 节点的度 节点所拥有的子树的个数. 树的度 树中各节点度的最大值 节点的深度 节点的深度等于祖先节点的数量 树的高度 树的高度 ...
- 数据结构☞二叉搜索树BST
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它可以是一棵空树,也可以是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它 ...
- 二叉搜索树的java实现
转载请注明出处 一.概念 二叉搜索树也成二叉排序树,它有这么一个特点,某个节点,若其有两个子节点,则一定满足,左子节点值一定小于该节点值,右子节点值一定大于该节点值,对于非基本类型的比较,可以实现Co ...
- 96题--不同的二叉搜索树(java、中等难度)
题目描述:给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 示例如下: 分析:本题可用动态规划的方法求解. 设 dp[n] 表示以 1 ... n 为节点组成的二叉搜索树的种类 ...
随机推荐
- dma驱动
http://www.crifan.com/files/doc/docbook/dma_pl08x_analysis/release/html/dma_pl08x_analysis.html#idp2 ...
- DIOCP之编写第一个应用程序(三)
Client 设计功能如下: 1.建立与服务器连接 2.请求连接时,加密密码,采用Base64编码 3.定时发送心跳告诉服务器在线(长连接,用于接收推送信息) 4.进行相关的数据处理与交互 第一步:创 ...
- RabbitMq 集群配置
1. RabbitMQ 所需的附属安装包 1.1 openGL安装 执行命令: [root@localhost local]# yum install mesa-libGL-devel mesa-l ...
- C语言获取时间
转载:http://www.cnblogs.com/fzhe/archive/2012/11/06/2757858.html C语言获取系统时间的几种方式 C语言中如何获取时间?精度如何? 1 使 ...
- Python SQLAlchemy --3
本文為 Python SQLAlchemy ORM 一系列教學文: 刪除 學會如何查詢之後,就能夠進行後續的刪除.更新等操作. 同樣地,以幾個範例做為學習的捷徑. 123456789 user_1 = ...
- CSS3径向渐变----大鱼吃小鱼之孤单的大鱼
最近迷恋上了钓鱼,可是总钓不到大鱼,所以就画条大鱼来安慰一下我这柔弱的心灵. 先上图: 上面这个就是今晚上我要跟大家分享的小DEMO,我给他起名字就“大鱼吃小鱼之孤单的大鱼”. 转入正题,这条大鱼分为 ...
- JS学习之函数内部属性和方法
知识点:arguments和this对象.caller属性.apply()和call()方法 arguments对象:函数内部对象,传入函数中所有参数的集合,类数组对象 属性:callee 指 ...
- 深度学习主机环境配置: Ubuntu16.04 + GeForce GTX 1070 + CUDA8.0 + cuDNN5.1 + TensorFlow
深度学习主机环境配置: Ubuntu16.04 + GeForce GTX 1070 + CUDA8.0 + cuDNN5.1 + TensorFlow 最近在公司做深度学习相关的学习和实验,原来一直 ...
- Oracle如何导入导出数据(转自)
导出:exp ssht/taxware@sshtfile=d:\ssht.dmpexp 用户名/密码@服务名导入:imp ssht/taxware@mysshtfile=d:\ssht.dmp fro ...
- 「脑洞」图片转HTML(支持动画)
也许是受到很久以前看到的这玩意儿的原因:The Shapes of CSS 现在开脑洞写了个自动转换,顺便支持了动画……嗯,纯 CSS (:з」∠) 主要步骤就是用 Python 读图片,然后把像素全 ...