关于什么是搜索二叉树我就不介绍了,我主要讲实现的思路。
我们来用这棵树来举例子
首先是搜索二叉树的添加
public void SearchTreeInsert(Search_Tree search_tree, int Key) throws Exception {//搜索二叉树插入 if (search_tree == null) { throw new Exception("Insert:SearchTree"); } if (search_tree.RightSon == null && search_tree.LeftSon == null && Flag == 0) {//Flag保证根节点只会被插入一次 search_tree.Key = Key; Flag++; return; } Search_Tree cur = search_tree;//cur保存搜索树的根节点 Search_Tree New_Node = new Search_Tree(Key);//创建新节点 while (true) { if (Key > cur.Key) {//如果要插入的值大于节点的值 if (cur.RightSon == null) {//如果节点没有右子树 cur.RightSon = New_Node;//那么新节点就作为右子树 return; } else {//如果节点有右子树 cur = cur.RightSon;//"根"节点就变为它的右子树 } } if (Key < cur.Key) {//如果要插入的值小于节点的值 if (cur.LeftSon == null) {//如果节点没有左子树 cur.LeftSon = New_Node;//那么新节点就作为左树 return; } else {//如果节点有左子树 cur = cur.LeftSon;//"根"节点就变为它的左子树 } } } }
在添加的时候没什么太大的需要注意的问题,因为我们已经知道了搜索二叉树的概念:在插入的时候如果是根节点就直接设置数值,我的代码里有一个Flag标记,这个标记的作用是防止头节点的二次设置,具体的我会在下面完整的代码里有注释。在其他情况下,插入时先用插入元素和当前节点做比较,来判断应该插入在节点的左子树还是右子树,之后如果左子树/右子树为空的时候就可以插入,否则把当前节点变为其左子树/右子树的的节点,循环此过程。最终就可以实现插入的需求了。
接下来我主要讲删除的操作,先看代码
public boolean Search_TreeDelete(Search_Tree search_tree,int Key)throws Exception {//二叉搜索树删除 if (search_tree == null) { throw new Exception("Delete:二叉搜索树不存在"); } Search_Tree cur = search_tree;//cur暂时保存根节点 Search_Tree Parent = search_tree; while (cur != null) {//寻找是否存在要删除的节点,如果最后cur为空,那么就说明找不到了 if (cur.Key == Key) { break; } if (Key > cur.Key) { Parent = cur;//每次都要储存父节点的位置 cur = cur.RightSon; } else { Parent = cur;//同上 cur = cur.LeftSon; } } if (cur == null) {//如果找不到元素说明删除失败 return false;//返回false } //能走到这一步说明找到了 Search_Tree To_Remove = cur;//To_Remove就是要删的节点 //现在分四种情况 //1:要删除的节点没有子树 if (To_Remove.LeftSon == null && To_Remove.RightSon == null) { if(To_Remove.Key > Parent.Key){ Parent.RightSon = null; }else{ Parent.LeftSon = null; } return true; } //2:要删除的节点只有左子树 if (To_Remove.LeftSon != null && To_Remove.RightSon == null) {//如果要删除的节点只有左子树 if (To_Remove.Key == search_tree.Key) {//如果要删除的是根节点,所以要分情况讨论 search_tree.Key = search_tree.LeftSon.Key; search_tree.LeftSon = search_tree.LeftSon.LeftSon; return true; } if (To_Remove.Key < Parent.Key) {//理由同上,只不过这次要删除的节点是父节点的左子树 Parent.LeftSon = To_Remove.LeftSon; return true; } } //3:要删除的节点只有右子树 if (To_Remove.RightSon != null && To_Remove.LeftSon == null) {//如果要删除的节点只有右子树 if (To_Remove.Key == search_tree.Key) {//如果要删除的是根节点,所以要分情况讨论 search_tree.Key = search_tree.RightSon.Key; search_tree.RightSon = search_tree.RightSon.RightSon; return true; } if (To_Remove.Key > Parent.Key) {//说明要删除的节点是其父节点的右子树 Parent.RightSon = To_Remove.RightSon;//就把要删除节点的右子树作为父节点的右子树 //由于没有栈内存指向,要删除的节点会被GC回收掉。 return true; } if (To_Remove.Key < Parent.Key) {//理由同上,只不过这次要删除的节点是父节点的左子树 Parent.LeftSon = To_Remove.RightSon; return true; } } //4:要删除的节点有左右子树 if (To_Remove.RightSon != null && To_Remove.LeftSon != null) {//要删除的节点有左右子树 Search_Tree smallestNode = To_Remove.RightSon;//我们需要找到要删除节点的的右孩子中最小的元素 Search_Tree smallestNodeParent = To_Remove;//储存最小的元素的父节点,如果最小的元素有右子树的话方便处理 while (smallestNode.LeftSon != null) { smallestNodeParent = smallestNode; smallestNode = smallestNode.LeftSon; }//现在已经找到了右孩子中最小的元素了 if (smallestNode == To_Remove.RightSon) {//这里要注意的一点是:如果一开始的右子树没有左子树,那么它就不是 // 它父节点(头节点)的左子树了。 smallestNode.LeftSon = To_Remove.LeftSon;//因为一开始就没有左子树,所以直接把左子树作为右子树的左子树 Parent.RightSon = smallestNode;//根节点变为其右子树 return true; }else {//头节点的右孩子是有左子树的,所以就一定是其父节点的左子树 smallestNodeParent.LeftSon = smallestNode.RightSon;//把最小元素的右孩子给其父节点的左子树 To_Remove.Key = smallestNode.Key;//将最小元素的值交给头元素,就完成删除了 return true; } } return false; }
删除有四种情况,我先介绍一下然后再一个一个详细说明。(找到删除的元素的情况下)
1:删除的元素没有任何子树
2:删除的元素只有左子树
3:删除的元素只有右子树
4:删除的元素有左右子树
1:没有子树
首先,我们找到了要删除的元素,这种情况最简单了,但是还是有一个特殊情况:删除的是头节点。在这种情况下(因为我用的不是递归的算法),所以头节点不存在父节点。所以我们要做一个特殊处理。将整个树变成初始化的状态就可以了。除此之外我们只需要知道要删除元素是父节点的左子树还是右子树,然后把父节点的子数置为空就可以了。
2.3:有左/右子树。
类似于链表的操作,我们还是要判断是否删除的是根节点,如果是的话,我们只要把左/右子树的数值赋给根节点然后再删除根节点的左右节点。就相当于把根节点的左/右节点当作根节点。
4:有左右子树
这种情况有点复杂,首先我们要找到它右节点中的最小元素节点,作为替换到根节点的节点。除此之外,还有两种情况,1:要删除的节点的右节点可能没有左节点。2:可能删除的是头节点。
这两种情况,又分出了四种情况。下面是我全部的实现代码。
import java.lang.reflect.Constructor; public class Main { public static void main(String[] args) throws Exception{ Class<?> cls = SearchTree_Test.class; SearchTree_Test searchTree_test = (SearchTree_Test) cls.newInstance(); Class<?> cls1 = SearchTree_Test.Search_Tree.class; Constructor constructor = cls1.getDeclaredConstructors()[0]; SearchTree_Test.Search_Tree search_tree = (SearchTree_Test.Search_Tree)constructor.newInstance(searchTree_test); searchTree_test.SearchTreeInit(search_tree); searchTree_test.SearchTreeInsert(search_tree,50); searchTree_test.SearchTreeInsert(search_tree,60); searchTree_test.SearchTreeInsert(search_tree,40); searchTree_test.SearchTreeInsert(search_tree,70); searchTree_test.SearchTreeInsert(search_tree,55); // searchTree_test.SearchTreeInsert(search_tree,45); searchTree_test.SearchTreeInsert(search_tree,52); searchTree_test.SearchTreeInsert(search_tree,53); searchTree_test.SearchTreeInsert(search_tree,80); searchTree_test.SearchTreeInsert(search_tree,65); // searchTree_test.SearchTreeInsert(search_tree,57); // searchTree_test.Search_TreeDelete(search_tree,53); // searchTree_test.Search_TreeDelete(search_tree,50); // searchTree_test.Search_TreeDelete(search_tree,60); searchTree_test.Search_TreeDelete(search_tree,60); } } class SearchTree_Test { static private int Flag = 0; class Search_Tree { public Search_Tree() {}//构建无参构造 public Search_Tree(int key) { this.Key = key; } int Key; Search_Tree LeftSon; Search_Tree RightSon; } public SearchTree_Test(){}; public void SearchTreeInit(Search_Tree search_tree) {//初始化搜索二叉树 search_tree.LeftSon = null; search_tree.RightSon = null; } public void SearchTreeInsert(Search_Tree search_tree, int Key) throws Exception {//搜索二叉树插入 if (search_tree == null) { throw new Exception("Insert:SearchTree"); } if (search_tree.RightSon == null && search_tree.LeftSon == null && Flag == 0) {//Flag保证根节点只会被插入一次 search_tree.Key = Key; Flag++; return; } Search_Tree cur = search_tree;//cur保存搜索树的根节点 Search_Tree New_Node = new Search_Tree(Key);//创建新节点 while (true) { if (Key > cur.Key) {//如果要插入的值大于节点的值 if (cur.RightSon == null) {//如果节点没有右子树 cur.RightSon = New_Node;//那么新节点就作为右子树 return; } else {//如果节点有右子树 cur = cur.RightSon;//"根"节点就变为它的右子树 } } if (Key < cur.Key) {//如果要插入的值小于节点的值 if (cur.LeftSon == null) {//如果节点没有左子树 cur.LeftSon = New_Node;//那么新节点就作为左树 return; } else {//如果节点有左子树 cur = cur.LeftSon;//"根"节点就变为它的左子树 } } } } public boolean Search_TreeDelete(Search_Tree search_tree,int Key)throws Exception {//二叉搜索树删除 if (search_tree == null) { throw new Exception("Delete:二叉搜索树不存在"); } Search_Tree cur = search_tree;//cur暂时保存根节点 Search_Tree Parent = search_tree; while (cur != null) {//寻找是否存在要删除的节点,如果最后cur为空,那么就说明找不到了 if (cur.Key == Key) { break; } if (Key > cur.Key) { Parent = cur;//每次都要储存父节点的位置 cur = cur.RightSon; } else { Parent = cur;//同上 cur = cur.LeftSon; } } if (cur == null) {//如果找不到元素说明删除失败 return false;//返回false } //能走到这一步说明找到了 Search_Tree To_Remove = cur;//To_Remove就是要删的节点 //现在分四种情况 //1:要删除的节点没有子树 if (To_Remove.LeftSon == null && To_Remove.RightSon == null) { if(To_Remove.Key > Parent.Key){ Parent.RightSon = null; }else if(To_Remove.Key > Parent.Key){ Parent.LeftSon = null; }else { search_tree.Key = 0; Flag--; } return true; } //2:要删除的节点只有左子树 if (To_Remove.LeftSon != null && To_Remove.RightSon == null) {//如果要删除的节点只有左子树 if (To_Remove.Key == search_tree.Key) {//如果要删除的是根节点,所以要分情况讨论 search_tree.Key = search_tree.LeftSon.Key; search_tree.LeftSon = search_tree.LeftSon.LeftSon; return true; } if (To_Remove.Key < Parent.Key) {//理由同上,只不过这次要删除的节点是父节点的左子树 Parent.LeftSon = To_Remove.LeftSon; return true; } } //3:要删除的节点只有右子树 if (To_Remove.RightSon != null && To_Remove.LeftSon == null) {//如果要删除的节点只有右子树 if (To_Remove.Key == search_tree.Key) {//如果要删除的是根节点,所以要分情况讨论 search_tree.Key = search_tree.RightSon.Key; search_tree.RightSon = search_tree.RightSon.RightSon; return true; } if (To_Remove.Key > Parent.Key) {//说明要删除的节点是其父节点的右子树 Parent.RightSon = To_Remove.RightSon;//就把要删除节点的右子树作为父节点的右子树 //由于没有栈内存指向,要删除的节点会被GC回收掉。 return true; } if (To_Remove.Key < Parent.Key) {//理由同上,只不过这次要删除的节点是父节点的左子树 Parent.LeftSon = To_Remove.RightSon; return true; } } //4:要删除的节点有左右子树 if (To_Remove.RightSon != null && To_Remove.LeftSon != null) {//要删除的节点有左右子树 Search_Tree smallestNode = To_Remove.RightSon;//我们需要找到要删除节点的的右孩子中最小的元素 Search_Tree smallestNodeParent = To_Remove;//储存最小的元素的父节点,如果最小的元素有右子树的话方便处理 while (smallestNode.LeftSon != null) { smallestNodeParent = smallestNode; smallestNode = smallestNode.LeftSon; }//现在已经找到了右孩子中最小的元素了 if (smallestNode == To_Remove.RightSon) {//这里要注意的一点是:如果一开始的右子树没有左子树,那么它就不是 // 它父节点(头节点)的左子树了。 if(search_tree.Key == To_Remove.Key){//如果删除的是根节点 To_Remove.Key = To_Remove.RightSon.Key; To_Remove.RightSon = To_Remove.RightSon.RightSon; }else { smallestNode.LeftSon = To_Remove.LeftSon;//因为一开始就没有左子树,所以直接把左子树作为右子树的左子树 Parent.RightSon = smallestNode;//根节点变为其右子树 return true; } }else {//头节点的右孩子是有左子树的,所以就一定是其父节点的左子树 smallestNodeParent.LeftSon = smallestNode.RightSon;//把最小元素的右孩子给其父节点的左子树 To_Remove.Key = smallestNode.Key;//将最小元素的值交给头元素,就完成删除了 return true; } } return false; } }具体的实现方法,我有详细的注释,就不在这里多说明了,大家可以结合代码与注释来理解这四种情况。