在我的博客:二叉搜索树的相关操作(递归实现)中对二叉搜索树进行了初始化、销毁以及插入、查找、删除操作!下面我将在其基础上对插入、查找、删除操作使用非递归的方式实现。
1. 按值插入
(1)空树时,直接插入在根节点
(2)非空树时:① 先找到合适的可以插入的空位置(循环找到空位置)
② 创建新节点
③ 判断将其插入在其父节点的左子树还是右子树
//思路:1.空树时,直接插入在根节点 // 2.非空树时:(1)先找到合适的可以插入的空位置 // (2)创建新节点 // (3)判断将其插入在其父节点的左子树还是右子树 void SearchTreeInsertEx(SearchNode** proot,SearchNodeType value) { //非法输入 if(proot==NULL) { return; } //1.空树时,直接插入在根节点 if(*proot==NULL) { SearchNode* new_node=CreateSearchNode(value); *proot=new_node; } //2.非空树时 //当前节点 SearchNode* cur=*proot; //当前节点的父节点 SearchNode* pre=NULL; while(1) { //判断是否找到合适的位置进行插入 if(cur==NULL) { break; } //(1)先找到合适的可以插入的空位置 if(value<cur->data) { //当value<cur->data时,继续在其左子树中找 pre=cur; cur=cur->lchild; } else if(value>cur->data) { //当value>cur->data时,继续在其右子树中找 pre=cur; cur=cur->rchild; } else { //当相等时,约定插入失败 return; } } //(2)创建新节点 SearchNode* new_node=CreateSearchNode(value); //(3)判断将其插入在其父节点的左子树还是右子树 if(value<pre->data) { //插入在其父节点的左子树中 pre->lchild=new_node; } else if(value>pre->data) { //插入在其父节点的右子树中 pre->rchild=new_node; } }
2. 按值查找
遍历二叉搜索树去查找:
(1)空树时直接return NULL
(2)非空树时,利用while循环去比较查找值与节点值的大小,此时又分为三种情况:
①当查找值<节点值时,在其节点的左子树中继续查找
②当查找值>节点值时,在其节点的右子树中继续查找
③当查找值=节点值时,说明找到了。
(3)需要注意的是,当遍历完二叉搜索树都没有找到,则表示找不到,直接return NULL
//思路:在二叉搜索树中遍历查找 //1.空树时直接return NULL //2.非空树时,利用while循环去比较查找值与节点值的大小 SearchNode* SearchTreeFindEx(SearchNode* root,SearchNodeType find) { //1.空树时直接return NULL if(root==NULL) { return NULL; } //2.非空树时 SearchNode* cur=root; while(cur!=NULL) { //循环判断是否找到find节点 if(find<cur->data) { //在左子树中继续查找 cur=cur->lchild; } else if(find>cur->data) { //在右子树中继续查找 cur=cur->rchild; } else { //找到了,跳出循环 break; } } return cur; //此时返回的cur有可能是找到的节点的指向也有可能是未找到的空指针NULL }
3. 按值删除
(1)找到要删除的节点
(2)分情况讨论要删除的节点:
①当要删除的节点无子树时,将其父节点对应的子树指向NULL
②当要删除的节点只有左子树时,将其父节点对应的子树指向要删除的节点的左子树
③当要删除的节点只有右子树时,将其父节点对应的子树指向要删除的节点的右子树
④当要删除的节点有左右子树时,找到要删除的节点的右子树的最小节点并用其data覆盖要删除的节点的data最后删除最小节点
//思路:1.找到要删除的节点 // 2.分情况讨论要删除的节点: // (1)当要删除的节点无子树时,将其父节点对应的子树指向NULL // (2)当要删除的节点只有左子树时,将其父节点对应的子树指向要删除的节点的左子树 // (3)当要删除的节点只有右子树时,将其父节点对应的子树指向要删除的节点的右子树 // (4)当要删除的节点有左右子树时,找到要删除的节点的右子树的最小节点并用其data覆盖要删除的节点的data最后删除最小节点 void SearchTreeRemoveEx(SearchNode** proot,SearchNodeType to_remove) { //非法输入 if(proot==NULL) return; //空树时 if(*proot==NULL) return; //1.找到要删除的节点 SearchNode* remove=*proot; //parent表示要删除节点的父节点 SearchNode* parent=NULL; while(1) { //没找到要删除的节点 if(remove==NULL) return; //当to_remove<remove->data时,在remove的左子树中继续找 if(to_remove<remove->data) { parent=remove; remove=remove->lchild; } //当to_remove>remove->data时,在remove的右子树中继续找 else if(to_remove>remove->data) { parent=remove; remove=remove->rchild; } //当相等时表示找到了 else { break; } } //2.判断要删除的节点的情况 //(1)要删除的节点没有子树时 if(remove->lchild==NULL&&remove->rchild==NULL) { //①要删除的节点为根节点时 if(remove==*proot) { *proot=NULL; } //②要删除的节点不是根节点时 else { //判断要删除的节点为其父节点的左子树还是右子树 if(remove->data<parent->data) { //为左子树 parent->lchild=NULL; } else { //为右子树 parent->rchild=NULL; } } DestroySearchNode(remove); } //(2)要删除的节点只有左子树时 else if(remove->lchild!=NULL&&remove->rchild==NULL) { //①要删除的节点为根节点时 if(remove==*proot) { *proot=remove->lchild; } //②要删除的节点不是根节点时 else { //判断要删除的节点为其父节点的左子树还是右子树 if(remove->data<parent->data) { //为左子树 parent->lchild=remove->lchild; } else { //为右子树 parent->rchild=remove->lchild; } } DestroySearchNode(remove); } //(3)当要删除的节点只有右子树时 else if(remove->lchild==NULL&&remove->rchild!=NULL) { //①要删除的节点为根节点时 if(remove==*proot) { *proot=remove->rchild; } //②要删除的节点不是根节点时 else { //判断要删除的节点为其父节点的左子树还是右子树 if(remove->data<parent->data) { //为左子树 parent->lchild=remove->rchild; } else { //为右子树 parent->rchild=remove->rchild; } } DestroySearchNode(remove); } //(4)要删除的节点有左右子树时 else { //①先找到其右子树中最小节点 SearchNode* min=remove->rchild; SearchNode* min_parent=remove; //注意这是二叉搜索树故最小的节点一定在最左边 while(min->lchild!=NULL) { min_parent=min; min=min->lchild; } //找到min后,说明min是其右子树中最左边的树,故min没有左子树 //②用最小节点min的值覆盖要删除的节点的值 remove->data=min->data; //③此时就是删除min节点 if(min->data<min_parent->data) { //注意:找到的min的左子树为空树,故直接将min的右子树托付给其父节点 min_parent->lchild=min->rchild; } else { min_parent->rchild=min->rchild; } DestroySearchNode(min); } }
4. 测试代码
以下代码是测试以上操作正确与否,以供参考!
/*=======测试代码=======*/ //测试SearchTreeTnsert //为了验证测试结果的正确与否,编写先序遍历和中序遍历函数 void PreOrder(SearchNode* root) { //空树 if(root==NULL) { return; } printf("%c ",root->data); PreOrder(root->lchild); PreOrder(root->rchild); } void InOrder(SearchNode* root) { //空树 if(root==NULL) { return; } InOrder(root->lchild); printf("%c ",root->data); InOrder(root->rchild); } void Test_SearchTreeInsert() { HEADER; SearchNode* root; SearchTreeInit(&root); SearchTreeInsertEx(&root,'a'); SearchTreeInsertEx(&root,'b'); SearchTreeInsertEx(&root,'f'); SearchTreeInsertEx(&root,'d'); SearchTreeInsertEx(&root,'e'); SearchTreeInsertEx(&root,'c'); printf("先序遍历结果:\n"); PreOrder(root); printf("\n"); printf("中序遍历结果:\n"); InOrder(root); printf("\n"); } //测试SearchNodeFind void Test_SearchTreeFind() { HEADER; SearchNode* root; SearchTreeInit(&root); SearchTreeInsertEx(&root,'a'); SearchTreeInsertEx(&root,'b'); SearchTreeInsertEx(&root,'f'); SearchTreeInsertEx(&root,'d'); SearchTreeInsertEx(&root,'e'); SearchTreeInsertEx(&root,'c'); SearchNode* ret=NULL; ret=SearchTreeFindEx(root,'a'); printf("expected a,actual %c\n",ret->data); ret=SearchTreeFindEx(root,'d'); printf("expected d,actual %c\n",ret->data); ret=SearchTreeFindEx(root,'h'); printf("expected NULL,actual %p\n",ret); } //测试SearchTreeRemove void Test_SearchTreeRemove() { HEADER; SearchNode* root; SearchTreeInit(&root); SearchTreeInsertEx(&root,'a'); SearchTreeInsertEx(&root,'b'); SearchTreeInsertEx(&root,'f'); SearchTreeInsertEx(&root,'d'); SearchTreeInsertEx(&root,'e'); SearchTreeInsertEx(&root,'c'); SearchTreeRemoveEx(&root,'c'); printf("先序遍历结果:\n"); PreOrder(root); printf("\n"); printf("中序遍历结果:\n"); InOrder(root); printf("\n"); } /*========主函数======*/ int main() { Test_SearchTreeInsert(); Test_SearchTreeFind(); Test_SearchTreeRemove(); return 0; }以上就是非递归实现的二叉搜索树的相关操作!