二叉树系列五:二叉查找树
(1)二叉查找树的定义
二叉查找树是一棵二叉树,如果树不为空,那么需满足如下的性质:
1)每一个元素都有一个关键字,且任何两个不同的元素的关键字不相等,即关键字惟一;
2)左子树(如果存在)中的关键字小于根节点的关键字;
3)右子树(如果存在)中的关键字大于根节点的关键字;
4)左右子树也是二叉查找树。
(2)二叉查找树的查找、删除和插入
a. 查找 二叉查找树的查找从根结点开始,若当前结点的关键字等于要查找的关键字,那么返回当前结点;如果当前结点的关键字小于要查找的关键字,那么从左子树中开始寻找,否则从右子树中开始寻找。C++代码实现如下:
1 template<class T> 2 TreeNode<T>* Tree<T>::Find(const T data) 3 { 4 TreeNode<T>* cur = root; 5 while(cur) 6 { 7 if(cur->data < data) 8 cur = cur->rightChild; 9 else if(cur->data > data) 10 cur = cur->leftChild; 11 else 12 return cur; 13 } 14 return NULL; 15 }
延伸:如果查找树中第k小的元素,那么可以为每个结点增加一个字段leftSize:这个字段为当前结点的左子树中的元素数目加1,即表示当前结点在整个树中的关键字的排列顺序,因而可以如下实现:
1 template<class T> 2 TreeNode<T>* Tree<T>::Find(int k) 3 { 4 TreeNode<T>* cur = root; 5 while(cur) 6 { 7 if(cur->leftSize > k) 8 cur = cur->leftChild; 9 else if(cur->data < k) 10 { 11 k -= cur->leftSize; 12 cur = cur->rightChild; 13 } 14 else 15 return cur; 16 } 17 return NULL; 18 }
b. 删除
利用查找的思路,从根节点开始查找要删除的关键字,并记录其父结点,如果找到了关键字,那么根据下面三种情况进行删除:
i) 如果要删除的结点为叶结点,那么只需将父结点指向叶结点的指针设为NULL并释放掉当前叶结点即可;
ii)如果要删除的结点只有一个子女,那么只需将当前结点的父结点对应的指针指向该子女即可,并释放当前结点;
iii)如果要删除的结点有两个子女,那么可以用其左子树的最大元素或者右子树的最小元素的关键字替代当前结点,然后根据替代的关键字将删除操作转移至对应的左子树或者右子树中,最终要被删除的结点要么是叶结点要么是只有一个子女的结点,即i)和ii)的情形,此时删除就很方便,代码实现如下:
1 template<class T> 2 bool Tree<T>:: Delete(TreeNode<T>* node, const T data) 3 { 4 TreeNode<T>* q = NULL; 5 TreeNode<T>* p = node; 6 while(p) 7 { 8 if(p->data > data) 9 { 10 q = p; 11 p = p->leftChild; 12 } 13 else if(p->data < data) 14 { 15 q = p; 16 p = p->rightChild; 17 } 18 else 19 { 20 TreeNode<T>* temp = NULL; 21 if(!p->leftChild || !p->rightChild) 22 { 23 if(p->leftChild && !p->rightChild) 24 { 25 temp = p->leftChild; 26 } 27 else if(!p->leftChild && p->rightChild) 28 { 29 temp = temp->rightChild; 30 } 31 if(q->leftChild == p) 32 q->leftChild = temp; 33 else 34 q->rightChild = temp; 35 delete p; 36 return true; 37 } 38 else 39 { 40 //寻找左子树的最大元素 41 temp = p->leftChild; 42 while(temp->rightChild) 43 temp = temp->rightChild; 44 p->data = temp->data; 45 Delete(p->leftChild,temp->data); 46 } 47 } 48 } 49 }
c. 插入
插入时,首先寻找要插入的关键字,如果找到那么不需要再插入,如果没有找到,那么将新的结点插入到查找过程中的最后一个结点位置处即可,实现如下:
1 template<class T> 2 bool Tree<T>:: Insert(const T data) 3 { 4 TreeNode<T>* p = root; 5 TreeNode<T>* q = NULL; 6 TreeNode<T>* newNode = new TreeNode<T>(data); 7 if(!root) 8 { 9 root = newNode; 10 return true; 11 } 12 while(p) 13 { 14 if(p->data > data) 15 { 16 q = p; 17 p = p->leftChild; 18 } 19 else if(p->data < data) 20 { 21 q = p; 22 p = p->rightChild; 23 } 24 else 25 return false; 26 } 27 if(q->data < data) 28 q->rightChild = newNode; 29 else 30 q->leftChild = newNode; 31 }
如果用h表示该二叉查找树的高度,那么上述的操作的时间复杂度都为O(h).