二叉排序树:是一种树,我们用这种结构存储数据
那么什么才算二叉排序树?
二叉排序树:
* 特点:又称为二叉查找树,要么是一个空树要么符合下面列性质
* 若左子树不为空,则左子树上的所有节点的值均小于它的根结构的值
* 若右子树不为空,则右子树上的所有节点的值均大于它的根结构的值
* 并且根节点的左右子树都要满足上面两个条件才称为二叉排序树
* 根据前两个性质可得二叉排序树不能有重复数值
* 这个树因为左边都是小数,中间都是中数,右边都是大数,所以我们直接通过中序遍历便可按顺序输出
* 目的:为了提高查找和插入删除关键字的速度
所有的步骤全在代码中写了,直接贴代码。
package sorttree;
import org.omg.Messaging.SYNC_WITH_TRANSPORT;
import java.util.ArrayList;
import java.util.List;
//二叉排序树
/*二叉排序树:
* 特点:又称为二叉查找树,要么是一个空树要么符合下面列性质
* 若左子树不为空,则左子树上的所有节点的值均小于它的根结构的值
* 若右子树不为空,则右子树上的所有节点的值均大于它的根结构的值
* 并且根节点的左右子树都要满足上面两个条件才称为二叉排序树
* 根据前两个性质可得二叉排序树不能有重复数值
* 这个树因为左边都是小数,中间都是中数,右边都是大数,所以我们直接通过中序遍历便可按顺序输出
* 目的:为了提高查找和插入删除关键字的速度
*
* 创建的算法我想了很久:这里说下它和创建普通二叉树的区别
*普通二叉树:创建时递归一次建立一个
*排序二叉树:它必须递归找到它该放的位置才建立
* */
class TreeNode{
int data;//数据域
TreeNode leftNode;//左子树
TreeNode rightNode;//右子树
}
public class SortTree {
static TreeNode treeNode = new TreeNode();//设置根节点为类变量。
static TreeNode cordNode = new TreeNode();//设置记录节点为类变量。
public static void main(String[] args) {
int i;
int[] sort = new int[]{42, 12, 23, 64, 74, 34, 76, 24, 53, 63, 6};//建立一个普通数组:
//TreeNode treeNode = new TreeNode();//设置根节点
treeNode.data = sort[0];//初始化根节点是数组第一个数
TreeNode parNode = new TreeNode();//设置记录双亲的节点
//TreeNode cordNode = new TreeNode();//设置记录最后的节点
for (i = 1; i < sort.length; i++)//对sort的所有元素进行排序,前面已经对第一个数赋值了,直接从第二个数开始操作
createSortTree(sort[i], treeNode);
printTree(treeNode);//中序遍历输出
System.out.println(" ");
searchRorF(treeNode, 25, parNode, cordNode);//查找顺序二叉树的点
// treeNode = insert(treeNode, 13);//第一种插入调用语句
treeNode = insert(treeNode, 13, cordNode);
printTree(treeNode);//中序遍历输出
System.out.println(" ");
treeNode=deletBST(treeNode,treeNode,42);
printTree(treeNode);
}
//需求:每次只确定sort数组中的一个单元,,对这个单元排序,
public static void createSortTree(int i, TreeNode treeNode) {//完成一个无序数列到一个二叉排序树的建立
/*建立思想:先选取第一个数字为根节点,依次读取顺序表的内容,将大于这个节点放在右子树,小于节点的放在左子树
* 通过循环遍历每个数,对这个数按照要求一直遍历整个树,直到找到适合它的位置,循环结束创建完成
*
* 参数:int[] sortTree用户输入的列表,获得
* */
if (i < treeNode.data)//判断节点是否小于当前根节点,是的话向左子树遍历
{
if (treeNode.leftNode == null) {//如果没有左子树,就要连接
TreeNode treeNode1 = new TreeNode();
treeNode1.data = i;
treeNode.leftNode = treeNode1;
} else {
createSortTree(i, treeNode.leftNode);
}
} else if (i > treeNode.data)//判断节点是否大于当前根节点,是的话向右子树遍历
{
if (treeNode.rightNode == null) {//如果没有右子树,就要连接
TreeNode treeNode1 = new TreeNode();
treeNode1.data = i;
treeNode.rightNode = treeNode1;
} else {
createSortTree(i, treeNode.rightNode);
}
}
}
public static void printTree(TreeNode treeNode) {//中序输出顺序二叉树 ,接受参数为排好序的二叉树顶点,
if (treeNode != null) {//如果递归到的节点为空,结束这层递归,返回上一次递归执行
printTree(treeNode.leftNode);//先一直搜寻左节点到最底部,当递归到底部的下一个节点时为空,我们通过最外层的控制语句结束这层递归
System.out.print(treeNode.data + " ");//当上面的那层递归结束到了这里说明当前节点是最后一个节点,输出这个节点便是最底层的左节点
printTree(treeNode.rightNode);//开始递归查询它的右节点,直到查到空为止
}
}
public static boolean searchRorF(TreeNode treeNode, int num, TreeNode parNode, TreeNode cordNode) {
if (treeNode == null)//第一次传进来判断传入的数是否为空,为空则直接返回false结束查找,之后传进来如果为空也说明找完了并没找到那个数
{
cordNode = parNode;//如果没找到,将双亲结点赋值给记录节点
return false;//返回false
} else if (treeNode.data == num)//再判断传入的点是否等于treeNode的data,如果相等则返回true退出。
{
cordNode = treeNode;//如果找到,则赋值给找到的节点
System.out.println("找到了要查找的数据");
return true;
} else if (num < treeNode.data) { //如果传入值小于当前结点数据,则去它的左子树查找。
return searchRorF(treeNode.leftNode, num, treeNode, cordNode);
} else { //如果传入值大于当前结点数据,则去它的右子树查找。
return searchRorF(treeNode.rightNode, num, treeNode, cordNode);
}
}
//第三种方法把存储结点设置为全局变量,就不实现了,因为还要改seach的方法
//第二种方法:insert里直接包含了查找位置的代码,这样可以直接获得记录节点,但是无法使用递归,只能使用迭代循环操作
//参数说明
public static TreeNode insert(TreeNode treeNode, int num, TreeNode cordNode) {
TreeNode insNode = new TreeNode();//初始化要插入的节点,
TreeNode operaNode;//操作用的点
operaNode=treeNode;//把根结点赋值给操作点,让操作点进行操作,保留根节点,最后返回
insNode.data = num;//赋值
if (operaNode == null) {//如果根节点为空直接将值赋给根节点
operaNode = insNode;
return treeNode;//完成操作返回这个数
}
while (operaNode != null) {//当节点为空时说明到了这个结点该插入的位置了
if (operaNode.data == num)//如果相等,则退出,不允许有重复值
return treeNode;//相等返回没操作的根节点就行了
else if (num < operaNode.data) {//如果查询数小于当前结点数
cordNode = operaNode;//先记录父节点
operaNode = operaNode.leftNode;//向左子树查询
} else {//如果查询数大于当前结点
cordNode = operaNode;//先记录父节点
operaNode = operaNode.rightNode;//向右子树查询
}
}
//循环完就得到了这个该插入的位置
//判断插入到哪里
System.out.println("插入"+cordNode.data);
if (num < cordNode.data) {//小于的话插入左子树
cordNode.leftNode = insNode;
} else {
cordNode.rightNode = insNode;
}
return treeNode;
}
/*删除算法的思路:
先通过递归找到要删除的元素
*要删除的结点如果只有左子树,删除这个点后连接左子树
*要删除的结点如果只有右子树,删除这个点后连接右子树
* 要删除的结点如果有左右子树,找到它的大于它的最小结点或者小于它的最大结点,然后替换,再删除结点,这时候这个结点的位置只有左或者右子树,再连接左右子树就行了
* 在中序遍历中他们三个结点相邻,所以选择他们替换不会对序列有任何影响
* */
/**
* rootNode再这里设置是因为最后要返回根节点
*treeNode在这里设置是操作的结点
* 算法思路:
* 先找到要删除的点,没找到的话就直接退出,找到的话通过delet函数进行删除
*
***/
public static TreeNode deletBST(TreeNode rootNode,TreeNode treeNode,int num) {
if (num != treeNode.data) {//如果没找到则结束
return rootNode;
}
else{
if(num==treeNode.data)//找到就进行删除操作
return delete(rootNode,treeNode);
else if (num < treeNode.data) {//如果小于去左子树
return deletBST(rootNode,treeNode.leftNode,num);
}
else
return deletBST(rootNode,treeNode.rightNode,num);
//如果小于去右子树
}
}
//删除操作思路:
//先判断是否只有左右子树,有的话直接删除
//如果左右子树都有,我们找到小于要删除点的最大结点记为max
//这个max点便是要删除点的下一个左结点的右子树的最后一个右结点
//如果max点有右子树 ,那么要拼接替换删除后的右子树
//如果max点只有左子树没有右子树,那么所找的点max只有左子树,替换后拼接左子树就好
public static TreeNode delete(TreeNode rootNode,TreeNode treeNode){
TreeNode operaNode;//用作这个节点向下遍历最后得到max结点,传进来的结点并不动
TreeNode cordNode;//这个节点用来记录max结点的前驱,功能代码中会讲
if(treeNode.rightNode==null) {//如果只有左子树
treeNode = treeNode.leftNode;//拼接
return rootNode;
}
else if (treeNode.leftNode==null) {//只有右子树
treeNode = treeNode.rightNode;//拼接
return rootNode;
}
else {//如果左右子树都有,先找到那个max点,就是要删除结点的下一个左结点的最右结点
cordNode=treeNode;//先将传入结点赋给操作结点以便进行接下来的操作
operaNode=treeNode.leftNode;//先获得要删除结点的左结点,
while(operaNode.rightNode!=null){//一直查找左结点的最后一个右结点,这个循环结束后,得到的opera为max结点
//保存当前结点为前驱节点后,向右子树继续遍历
cordNode=operaNode;
operaNode=operaNode.rightNode;
}
treeNode.data=operaNode.data;//把查到的max的值覆盖到要删除节点
if (cordNode!=treeNode){//如果前驱结点不等于删除位置结点,说明刚开始的要删除的结点的左结点有右结点,这是max是最后一个右结点,并没有右子树
cordNode.rightNode=operaNode.leftNode;//我们把max结点的左子树赋值给前驱结点的右子树后,相当于去除了这个结点
return rootNode;
}else {//到这步说明刚开始的要删除的结点的左结点没右结点,我们只需要将左子树拼接就好
cordNode.leftNode = operaNode.leftNode;
return rootNode;
}
}
}
}
这里补充java是可以通过对象参数传递的
//第一种:两个查找法。我又写了一个seach方法,专门返回记录的结点,通过两个seach方法结合使用
//先通过返回boolean值的seach方法来确定是否有相同值,再通过返回记录结点的seach方法返回记录结点。
//然后进行判断插入操作
/// /顺序二叉树数的查找。接受一个数字,每次判断它是大于还是小于当前点,如果大于去这个点的右子树寻找,如果小于到这个数的左子树寻找
// 直到最后判断了所有的点
//参数说明:
// TreeNode treeNode:根节点 int num:查找的数
// TreeNode parNode:记录双亲节点用 TreeNode cordNode最后返回的节点(如果没找到就是最后查找的哪个null节点的双亲,找到的话就是找到的那个元素)
// 一开始parnode为null,因为根节点没有双亲结点
// public static TreeNode searchNode(TreeNode treeNode,int num,TreeNode parNode,TreeNode cordNode){
// if(treeNode==null)//第一次传进来判断传入的数是否为空,为空则直接返回false结束查找,之后传进来如果为空也说明找完了并没找到那个数
// {
// cordNode=parNode;//如果没找到,将双亲结点赋值给记录节点
// System.out.println("没找到要查找的数据");
// return cordNode;//返回记录结点
//
// }
// else if(treeNode.data==num)//再判断传入的点是否等于treeNode的data,如果相等则返回true退出。
// {
// cordNode=treeNode;//如果找到,则赋值给找到的节点
// System.out.println("找到了要查找的数据");
// return cordNode;
// }
// else if (num<treeNode.data) { //如果传入值小于当前结点数据,则去它的左子树查找。
// return searchNode(treeNode.leftNode, num,treeNode,cordNode);
// }
// else { //如果传入值大于当前结点数据,则去它的右子树查找。
// return searchNode(treeNode.rightNode,num,treeNode,cordNode);
// }
// }
/*方法说明:
* 方法思路:先查找节点,如果查找到不操作,因为排序二叉树不允许有重复数字
* 没查找到节点,这时cordTree会返回查到最后返回的空节点的双亲节点,也就是我们要插入的地方
* 如果这个记录节点为null,说明排序二叉树为空,当作根节点使用
* 如果这个值小于当前记录节点,当作cord的左子树
* 如果这个值大于当前记录节点,当作cord的右子树
* 方法参数:接受排序二叉树根节点和要插入的数
* */
// public static TreeNode insert(TreeNode treeNode,int ins){
// TreeNode parNode=new TreeNode();//设置记录双亲的节点
// TreeNode cordNode=new TreeNode();//设置最后获得元素的节点
// TreeNode insNode=new TreeNode();//初始化要插入的节点
// insNode.data=ins;//赋值
// if(!searchRorF(treeNode,ins,parNode,cordNode)) {//先查找节点,如果查找到不操作,因为排序二叉树不允许有重复数字
// cordNode=searchNode(treeNode,ins,parNode,cordNode);
// if (cordNode == null) //如果这个记录节点为null,说明排序二叉树为空,当作根节点使用
// treeNode = insNode;//将这个要插入的结点当作根结点
// else if (ins < cordNode.data) { //如果这个值小于当前记录节点,当作cord的左子树
// cordNode.leftNode = insNode;
// }
// else { //如果这个值大于当前记录节点,当作cord的右子树
// cordNode.rightNode = insNode;
// }
// return treeNode;
// }else {
// return treeNode;//有关键字退出
// }
// }
// }
创建的算法我想了很久:这里说下它和创建普通二叉树的区别
普通二叉树:创建时递归一次建立一个
排序二叉树:它必须递归找到它该放的位置才建立
这里着重说下插入方法,我看的大话结构里用的是c语言可以直接用指针操作地址,因此可以在插入操作里直接调用查询操作找到记录点进行插入,而java无法直接操作地址,只能通过返回值,问题在于返回值只能返回一个,我既需要查询里判断是否查到的false/true值,又需要记录结点,所以这条路走不通。
我想了三种办法:
1.最简单的方法,修改根结点和记录节点都为全局变量,这样就可以直接在函数里对结点进行操作。
2.写两个查询方法,一个返回true/false,另一个返回记录结点,通过两个返回值便可进行操作,但是时间复杂度很高
3.可以把查询的方法融入到insert的方法,这样不用再查询一次,提高程序效率。