由于非递归函数的执行效率高,一般采用非递归的迭代方式来实现查找。
//迭代查找 struct node *IterFind(ElementType key,struct node *BST){ while(BST){ if(key > BST->data){ BST = BST->right; }else if(key < BST->data){ BST = BST->left; }else{//key == BST->data return BST;//返回查找成功的结点的地址 } } return NULL;//查找失败 }根据二叉搜索树的性质,最小元素一定在二叉树的最左分支的叶子结点上。同理,最大元素一定在二叉树的最右分支的叶子节点上。
//递归查找最小元素 struct node *FindMin(struct node *BST){ if(!BST) return NULL;//空的二叉树返回NULL if(!BST->left) return BST;//找到最左边结点 else return FindMin(BST->left);//沿着左分支继续查找 } //迭代查找最小元素 struct node *IterFindMin(struct node *BST){ if(BST){ while(BST->left){ BST = BST->left;//沿着左分支继续查找 } } return BST; }BST的删除分为三种情况: 1.待删除的结点是叶子结点: 直接删除,将指向该结点的指针置空即可 2.待删除的结点只有一个分支: 修改该结点的指针,将其指向为其孩子的结点。即上移子树。 3.待删除结点有左右两个分支: 这时有两种情况可以选择:用删除节点的直接前驱或者直接后继来替换当前结点,再递归删除直接前驱或者直接后继的结点位置。 (可能,十分绕口) 简而言之:一种就是选择其左子树的最大元素,另一种就是选择右子树的最小元素。 (注:用中序遍历一下上图的二叉树,你就知道为什么叫前驱元素和后继元素了。)
//删除 struct node *Delete(ElementType key,struct node *BST){ struct node *temp = NULL; if(!BST){ printf("要删除的元素未找到"); }else if(key < BST->data){ BST->left = Delete(key,BST->left); }else if(key > BST->data){ BST->right = Delete(key,BST->right);//找到要删除的结点 }else if(BST->left && BST->right){//被删除的结点有左右两个子结点 temp = FindMin(BST->right);//在右子树中找到最小的元素填充删除的结点 BST->data = temp->data; BST->right = Delete(BST->data,BST->right); }else if(BST->left){//被删除的结点只有左分支 BST = BST->left; }else if(BST->right){//被删除的结点只有右分支 BST = BST->right; }else{//被删除的结点无左右分支 BST = NULL; } return BST; }最好的情况是二叉排序树的形态和二分查找的判定树相同,其平均查找长度和log 2 (n)成正比。 最坏情况下,当先后插入的关键字有序时,构成的二叉排序树退化为单支树,树的深度为其平均查找长度(n+1)/2(和顺序查找相同)
为了避免二叉搜索树退化为单支树的情况,可以自平衡成为AVL树或红黑树,使查找树的高度为O(logn)
