我们这样规定:当我们插入一个新的结点时,这个结点的关键字(也就是我们常说的结点的值)如果小于当前所在位置的结点,则插入到这个结点的左侧,否则插入到这个结点的右侧,这样构建出来的二叉树,总会有: 一个结点的左子树的所有结点的值总是大于右子树所有结点的值
Insert
插入一个结点非常简单,我们只需要在插入的时候比较值的大小插入即可,这个插入结点是一个叶子结点
Delete
删除结点的时候,如果是叶子结点,直接删除,如果是分支结点,那我们必须在删除之后仍然保持有序。
假如我们规定一个结点的左子树中的所有结点的关键字都小于右子树所有结点的关键字,那么这个时候,当我们准备删除一个分支结点时,我们采取的策略是:找到这个分支结点左子树中的值最大的叶子结点或者是右子树中值最小的一个叶子结点,用这个叶子结点的值替换这个分支结点的值,删除这个叶子结点,这样,我们就不需要考虑这个分支结点前驱后继结点的复杂关系,并且可以保证结点有序
首先我们判断:
- 如果有左子树,找到左子树中最大的那个结点,将这个分支结点的数据替换为该结点的数据,删除掉该结点,保留分支结点
- 如果没有左子树,找到右子树中最小的结点,进行同样的操作
void delet(ElementType value){
Node* p = this->root;
while(p != NULL){
if(p->value > value){
p = p->right;
else if(p->value < value}{
p =p->left;
else{
// 左子树存在
if(p->left != NULL){
Node* q = p->left;
Node* parent = p;
// 找到左子树值最大的结点,同时记录该结点的前驱结点
for(;q->right != NULL;parent=q,q=q->right);
// 将p结点的值替换为左子树中值最大的结点的值
p->value = q->value;
// 如果这个结点就是p结点的左结点,则让p结点指向左结点的左结点(可以为空),删除左结点
if(parent == p){
p->left = p->left->left;
}
// 如果是p结点左结点的右子树最右端的一个结点,让这个结点的前驱结点的右指针指向这个最右端结点的左子树,可以为空
else{
parent->right = q->left;
}
// 最后,删除结点值替换了p结点值的q结点
delete q;
}
// 左子树不存在,右子树存在
else if(p->right != NULL){
Node* q = p->right;
Node* parent = p;
// 找到右子树值最小的结点,同时记录该结点的前驱结点
for(;q->left != NULL;parent-q,q=q->left);
// 将p结点的值替换为该结点的右子树中值最小的结点的值
p->value = q->value;
// 如果这个结点就是p结点的右结点,则让p结点指向右结点的右结点(可以为空),删除右结点
if(parent == p){
p->right = p->right->right;
}
// 如果是p结点右结点的左子树最左端的一个结点,让这个结点的前驱结点的左指针指向这个最左端结点的右子树,可以为空
else{
parent->left = q->right;
}
// 最后,删除结点值替换了p结点值的q结点
delete q;
}
// 是一个叶子结点
else{
// 判断这个叶子结点是不是根结点
if(p == root){
root = NULL;
}
delete p;
}
std::cout << "delete value: " << e << " successfully!" << std::endl;
}
}
if(i > MAX_SIZE || this->data[i] == DEFAULT_VALUE){
std::cout << "Failed to delete value: " << e << ", no such element." << std::endl;
return;
}