C语言实现搜索二叉树(无重复)

        关于什么是搜索二叉树我就不介绍了,我主要讲实现的思路。

我们来用这棵树来举例子


        首先是搜索二叉树的添加

    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;
    }

}


        具体的实现方法,我有详细的注释,就不在这里多说明了,大家可以结合代码与注释来理解这四种情况。



   

猜你喜欢

转载自blog.csdn.net/qq_38449518/article/details/80328854