二搜索叉树的查找主要涉及到查找指定的元素节点、最大最小值查找,查找指定节点的前驱节点或者后继节点。下面分别介绍。
二搜索叉树查找一个给定值key的过程与二分查找很类似,其过程为:首先是关键字key与树根的关键字进行比较,如果key大比根的关键字大,则在根的右子树中查找,否则在根的左子树中查找,重复此过程,直到找到或遇到空结点为止,如下图就是查找key为2节点过程。
根据查找过程,下面给出了递归和非递归的代码实现如下:
//查找值为key的节点,递归版本Node* bstree_search(BSTree root, Type key){ if (root==NULL || root->key==key) return root; if (key < root->key) return bstree_search(root->left, key); else return bstree_search(root->right, key);} //查找值为key的节点,非递归版本Node* iterative_bstree_search(BSTree root, Type key){ while ((root!=NULL) && (root->key!=key)) { if (key < root->key) root = root->left; else root = root->right; } return root;}根据二叉搜索树的性质,很容易想到:一颗非空的二叉搜索树查找其最大值流程很简单:只需要从根节点递归的遍历到右子树节点即可。当遍历到节点的右孩子为NULL时,则这个节点就是树的最大值,如下图所示。
同理,查找其最小值流程类似:从根节点递归的遍历到左子树节点即可。当遍历到节点的左孩子为NULL时,则这个节点就是树的最小值。
下面给出了查找最大值的代码实现,最小值类似,大家可以自己试着实现最小值查找。
//查找最大值Node* bstree_maximum(BSTree root){ if (root == NULL) return NULL; while(root->right != NULL) root = root->right; return root;}这里说的一个节点的前驱节点和后继节点指的是中序遍历顺序下某个节点的前驱和后继;更为详细的说就是:对于二叉搜索树,某一结点x的前驱就是小于key[x]的所有关键字中最大的那个结点,后继即是大于key[x]中的所有关键字中最小的那个结点。
查找前驱步骤:
(1)先判断节点x是否有左子树,如果有左子树则其左子树的最大节点即是x的前驱;
(2)如果没有左子树,但是该节点是其父节点的右孩子,那么父节点就是该节点的前驱结点;
(3)如果没有左子树,但是该节点是其父节点的左孩子,那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的右边孩子,那么Q就是该节点的前驱节点。
查找后继节点的步骤:
(1)先判断节点x是否有右子树,如果有右子树则其右子树的最小节点即是x的前驱;
(2)如果没有右子树,但是该节点是其父节点的左孩子,那么父节点就是该节点的后继结点;
(3)如果没有右子树,但是该节点是其父节点的右孩子,那么需要沿着其父亲节点一直向树的顶端寻找,直到找到一个节点P,P节点是其父节点Q的左边孩子,那么Q就是该节点的后继节点。
对于第三种情况,可能光看文字还是有点抽象,还是用图来示范吧,例如下图查找结点13的后继结点15的过程:
下面给出了前驱节点查找的代码实现,后继节点类似,大家可以自己试着实现后继节点查找代码。
//查找节点x的前驱节点Node* bstree_predecessor(Node *x){ /* 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。*/ if (x->left != NULL) return bstree_maximum(x->left); // 如果x没有左孩子。则x有以下两种可能: // (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。 /* (02) x是"一个左孩子",则查找"x的最低的父结点, 并且该父结点要具有右孩子",找到的这个"最低的父结点" 就是"x的前驱结点"。*/ Node* y = x->parent; while ((y!=NULL) && (x==y->left)) { x = y; y = y->parent; } return y;} 。*/ Node* y = x->parent; while ((y!=NULL) && (x==y->left)) { x = y; y = y->parent; } return y;}