二叉查找树的插入,寻找前驱结点、寻找后继结点、搜索、删除操作
二叉查找树的性质:对于树中的每个结点X,它的左子树中的所有项的值小于X中的项,而它的右子树中所有项的值大于X中的项。如下图中的两棵树,左边的为二叉查找树,而右边的树则不是。
二叉查找树的具体操作代码实现:
创建的树形结构如下图:
1.首先创建一个二叉树结点的类
template <class T>
class BSTNode{
public:
T data;
BSTNode *left;
BSTNode *right;
BSTNode *parent;
//BSTNode 的构造函数
BSTNode(T value,BSTNode *p,BSTNode *l,BSTNode * r):data(value),parent(),left(l),right(r){}
};
2.创建一个二叉查找树的类
template <class T>
class BinarySearchTree{
private:
BSTNode<T> *mRoot;
public:
BinarySearchTree();
~BinarySearchTree();
//将结点T插入到二叉树中
void insert(T data);
// 前序遍历"二叉树"
void preOrder();
// 中序遍历"二叉树"
void inOrder();
//删除结点T
void remove(T data);
//查找结点T
BSTNode<T>* search(T data);
//查找某一键值的前驱结点
BSTNode<T>* searchPreNode(T data);
//查找某一键值的后继结点
BSTNode<T>* searchSucceedNode(T data);
private:
void insert(BSTNode<T>* &tree,BSTNode<T>* T);
void recursionInsert(BSTNode<T>* &tree,BSTNode<T>* data,BSTNode<T>* parent);
int remove(BSTNode<T>* T);
void remove(BSTNode<T>* TNode,T data);
BSTNode<T>* search(BSTNode<T>* &tree,T data);
BSTNode<T>* searchPreNode(BSTNode<T>* &tree,BSTNode<T>* node);
BSTNode<T>* searchSucceedNode(BSTNode<T>* &tree,BSTNode<T>* node);
void preOrder(BSTNode<T>* tree) const;
void inOrder(BSTNode<T>* tree) const;
};
3.二叉查找树的插入操作:
//递归插入
template <class T>
void BinarySearchTree<T>::recursionInsert(BSTNode<T>* &tree,BSTNode<T>* t,BSTNode<T>* parent){
if(tree == NULL){
tree = t;
tree->parent = parent;
}else if(tree->data < t->data){
recursionInsert(tree->right,t,tree);
}else if(tree->data > t->data){
recursionInsert(tree->left,t,tree);
}
}
//插入结点的接口
template <class T>
void BinarySearchTree<T>::insert(T data){
BSTNode<T> *node = NULL;
node = new BSTNode<T>(data,NULL,NULL,NULL);
//如果新建结点失败,则返回
if(node == NULL){
return;
}
recursionInsert(mRoot,node,NULL);
}
4.二叉查找树的搜索操作
//递归实现查找结点
template <class T>
BSTNode<T>* BinarySearchTree<T>::search(BSTNode<T>* &tree,T data){
if(tree == NULL || tree->data == data){
return tree;
}else if(tree->data > data){
search(tree->left,data);
}else if(tree->data < data){
search(tree->right,data);
}
}
//查找结点的接口
template <class T>
BSTNode<T>* BinarySearchTree<T>::search(T data){
return search(mRoot,data);
}
5.寻找某一键值的前驱结点和后继结点(按照结点的**中序遍历**顺序设定)
前驱结点和和后继结点定义:
前驱结点:结点val值小于该节点val值,并且值最大的结点;
后继结点:结点val值大于该节点val值,并且值最小的结点;
寻找规则:
寻找前驱结点:
1.若一个结点有左子树,那么该节点的前驱结点是其左子树中val值最大的结点(也就是左子树中所谓的rightMostNode)
2.若一个结点没有左子树,那么判断该节点和其父节点的关系:
2.1.若该结点是其父节点的右边孩子,那么该结点的前驱结点即为其父节点;
2.2.若该节点是其父节点的左边孩子,那么需要沿着其父亲结点一直向树的顶端寻找,直到找到一个结点P,P结点是其父节点Q的右边孩子,那么Q就是该结点的前驱结点。
寻找后继结点:
1.若一个结点有右子树,那么该节点的后继结点是其右子树中val值最小的结点
2.若一个结点没有右子树,那么判断该结点与其父节点的关系:
2.1.若该结点是其父节点的左边孩子,那么该结点的后继结点即为其父节点
2.2.若该节点是其父节点的右边孩子,那么需要沿着其父节点一直向树的顶端一直寻找,知道找到一个结点P,P结点是其父节点Q的左边孩子,那么Q就是该节点的后继结点
//查找结点的前驱结点 template <class T> BSTNode<T>* BinarySearchTree<T>::searchPreNode(BSTNode<T>* &tree,BSTNode<T>* node){ BSTNode<T>* leftTreeNode = node->left; if(tree == NULL){ return tree; } //当该节点有左子树时,则该节点的前驱为左子树中val值最大的结点 if(leftTreeNode != NULL){ //findedNode = searchPreNode(mRoot,node->right); while(leftTreeNode->right != NULL){ leftTreeNode = leftTreeNode->right; } return leftTreeNode; }else{ //当该节点没有左子树,则判断该节点是起父节点的右边孩子,那么该节点的前驱结点即为其父节点 //若该节点昰其父节点的左边孩子,那么需要沿着其父亲结点一直向树的顶端寻找,直到找到节点P,P节点是其父节点的Q的右边孩子 while((node->parent != NULL)&&(node->parent->right != node)){ node = node->parent; } return node->parent; } } //查找前驱结点的接口 template <class T> BSTNode<T>* BinarySearchTree<T>::searchPreNode(T data){ BSTNode<T>* node = search(data); return searchPreNode(mRoot,node); } //查找后继结点 template<class T> BSTNode<T>* BinarySearchTree<T>::searchSucceedNode(BSTNode<T>* &tree,BSTNode<T>* node){ //查找结点右子树的根结点 BSTNode<T>* rightTreeNode = node->right; if(rightTreeNode != NULL){ while(rightTreeNode->left != NULL){ rightTreeNode = rightTreeNode->left; } return rightTreeNode; }else{ while((node->parent != NULL)&&(node->parent->left != node)){ node = node->parent; } return node->parent; } } //查找后继结点的接口 template<class T> BSTNode<T>* BinarySearchTree<T>::searchSucceedNode(T data){ BSTNode<T>* node = search(data); return searchSucceedNode(mRoot,node); }
6.二叉查找树的删除操作
//删除结点 /*在二叉查找树中删除一个给定的结点p有三种情况 (1)结点p无左右子树,则直接删除该结点,修改父节点相应指针 (2)结点p有左子树(右子树),则把p的左子树(右子树)接到p的父节点上 (3)左右子树同时存在,则有三种处理方式 a.找到结点p的中序直接前驱结点s,把结点s的数据转移到结点p,然后删除结点s,由于结点s为p的左子树中最右的结点,因而s无右子树,删除结点s可以归结到情况(2)。严蔚敏数据结构P230-231就是该处理方式。 b.找到结点p的中序直接后继结点s,把结点s的数据转移到结点p,然后删除结点s,由于结点s为p的右子树总最左的结点,因而s无左子树,删除结点s可以归结到情况(2)。算法导论第2版P156-157该是该处理方式。 c.找到p的中序直接前驱s,将p的左子树接到父节点上,将p的右子树接到s的右子树上,然后删除结点p。*/ template <class T> int BinarySearchTree<T>::remove(BSTNode<T>* node){ BSTNode<T> *p = NULL; BSTNode<T>* parent = node->parent;//需要删除节点的父节点 BSTNode<T>* s; if((node->left == NULL)&&(node->right == NULL)){ p = node; node = NULL; if(p == parent->left){ parent->left = NULL; }else{ parent->right = NULL; } }else if((node->left != NULL)&&(node->right == NULL)){ p = node; //将节点的左子树上移 node = node->left; //将左子树上移后,需要将删除结点的左(右)孩子结点连接到左子树的结点,此处代码很重要,若不添加,则会导致再次中序遍历此树的时候出错 if(p == parent->left){ parent->left = p->left; }else{ parent->right = p->left; } delete(p); //delete(p); }else if((node->left == NULL)&&(node->right != NULL)){ //node = node->right; //node->parent = p->parent; //node->parent->right = node->right; p = node; //将节点的右子树上移 node = node->right; //将右子树上移后,需要将删除结点的左(右)孩子结点连接到左子树的结点,此处代码很重要,若不添加,则会导致再次中序遍历此树的时候出错 if(p == parent->left){ parent->left = p->right; }else{ parent->right = p->right; } delete(p); }else{ //找到删除结点node的直接前驱s,把节点s的数据转移到node,然后删除结点s //采用第三种方法:找到p的中序直接前驱s,将p的左子树接到父节点上,将p的右子树接到s的右子树上,然后删除结点p。 p = node; //s为要删除的node结点的直接前驱结点 s = searchPreNode(node->data); //将P的左子树借到父亲节点上,则判断P是其父节点的左孩子还是右孩子 //如果删除的node是父节点的左孩子 node->data = s->data; remove(s); } return true; } //删除结点的接口 template <class T> void BinarySearchTree<T>::remove(T data){ BSTNode<T>* node = search(data); //BSTNode<T>* deleteNode; if(node != NULL){ remove(node); } }
6.二叉查找树的前序遍历
7.二叉查找树的中序遍历//二叉查找树的前序遍历(递归实现) template <class T> void BinarySearchTree<T>::preOrder(BSTNode<T>* tree) const{ if(tree == NULL){ return; } //printf("%d",tree->data); cout << tree->data << " "; preOrder(tree->left); preOrder(tree->right); } //二叉查找树的前序遍历接口 template <class T> void BinarySearchTree<T>::preOrder() { preOrder(mRoot); }
8.测试代码//二叉查找树的中序遍历(递归实现) template <class T> void BinarySearchTree<T>::inOrder(BSTNode<T>* tree) const{ if(tree == NULL){ return; } //printf("%d",tree->data); inOrder(tree->left); cout << tree->data << " "; inOrder(tree->right); } //二叉查找树的中序遍历接口 template <class T> void BinarySearchTree<T>::inOrder() { inOrder(mRoot); }
9.测试结果:int _tmain(int argc, _TCHAR* argv[]) { //static int arr[]= {1,5,4,3,2,6}; //static int arr[]= {15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9};62,88,58,47,35,73,51,99,37,93 //static int arr[]= {10,18,3,6,12,2,4}; static int arr[]= {62,88,58,47,35,73,51,99,37,93,29,36,49,48,50,56}; int i,ilen; ilen = sizeof(arr)/sizeof(arr[0]); BinarySearchTree<int>* tree = new BinarySearchTree<int>(); for(i = 0;i < ilen;i++){ cout << arr[i] << " "; tree->insert(arr[i]); } cout << "\n== 前序遍历: "; tree->preOrder(); cout << "\n== 中序遍历: "; tree->inOrder(); BSTNode<int>* node = new BSTNode<int>(5,NULL,NULL,NULL); node = tree->searchPreNode(51); if(node == NULL){ cout << "\n== 该结点51无前驱! "; }else{ cout << "\n== 该结点51的前驱为: "; cout << node->data << " "; } node = tree->searchSucceedNode(37); if(node == NULL){ cout << "\n== 该结点37无后继! "; }else{ cout << "\n== 该结点37的后继为: "; cout << node->data << " "; } //删除节点 cout << "\n== 删除的结点为47: "; tree->remove(47); cout << "\n== 删除后中序遍历: "; tree->inOrder(); system("pause"); return 0; }