二分搜尋樹系列之「 節點刪除 (remove) 」

ice_moss發表於2021-05-20

在這一小節中,我們會介紹二分搜尋樹中如何查詢最小最大值、最小最大值的刪除、刪除任意節點(刪除只有左孩子的節點、刪除只有右孩子的節點和刪除左右孩子都存在的節點);下面我們一一講解:

一. 查詢最小最大值及其刪除

  1. 查詢最小最大值
    其實很簡單,首先我們想一想二分搜尋樹的定義就會明白,最小值在以跟節點的左孩子節點的左孩子節點………上,看圖吧:

二分搜尋樹系列之【 節點刪除 (remove) 】

直接看程式碼吧!
在public中定義:

// 尋找二分搜尋樹的最小的鍵值
  Node* minmum(){
    assert(count != 0);
  Node* minnode = minmum(root);
 return minnode->left;
 }

在private中定義:

 // 尋找二分搜尋樹的最小的鍵值
Node* minmum(Node* node){
        if(node != NULL){
            minmum(node->left);
  }
        return node;

對於最大值嘛,邏輯一樣的這裡就省略了
直接上程式碼吧!
在public中定義:

// 尋找二分搜尋樹的最大的鍵值
Node* maxmum(){
    assert(count != 0);
  Node* maxnode = maxmum(root);
 return maxnode ->right;
}

在private中定義:

// 尋找二分搜尋樹的最大的鍵值
Node* maxmum(Node* node){
    if(node != NULL){
        maxmum(node->right);
  }
    return node;
}

2.刪除最小值最大值
以最大值為例:
其實就是將最大值找到,然後刪除(
二分搜尋樹系列之【 節點刪除 (remove) 】

我們在public中定義:

//刪除最大值的node
void removeMax(){
    if(root){
        root = removeMax(root);
        }
}

在private中定義:

//從二分搜尋樹中刪除最大值所在的節點
Node* removeMax(Node* node){
    if(node->right == NULL){
        Node* NODE = node->left;
       delete node;
       count--;
       return NODE;
  }
    node->right = removeMax(node->right);
    return node;
}

同理,刪除最小值也就是將最小值查詢到,然後刪除:
我們依然在public中定義:

void removeMin(){
       if(root){
           root = removeMin(root);
       }
}

在private中定義:

Node* removeMin(Node* node){
        if(node->left == NULL){
              Node* NODE = node->right;
              delete node;
              return NODE;
        }
        node->left =  removeMin(node->left)return node; 
}

二.刪除二分搜尋樹中任意節點

  1. 情況一:刪除只有左孩子(右孩子)的節點
    例如下圖,我們刪除節點58,但此時它存在左孩子,而從二分搜尋樹的定義可知如果將58刪除,就應該將50節點作為41節點的右孩子節點;所以我們需要在刪除58節點之前將50節點變成41節點的右孩子。

二分搜尋樹系列之【 節點刪除 (remove) 】
最後41節點的右子樹應該變成:

        41
          \
           50  
          /   \
        42     53   

同理對於只有右孩子的節點是相同的邏輯(在這裡省略)
下面看程式碼:(c++實現)
在public中定義:

//刪除二分搜尋樹中值的任意節點
void remove( Key key){
    root = remove(root, key);
}

在private中定義:

//刪除二分搜尋樹中值的任意節點
Node* remove(Node* node, Key key){
    //判斷node是否為空
  if(node == NULL) {
        return NULL;
  }

   //先找到需要刪除的值的node
  else if(key < node->key) {      //key為需要刪除的,node->key為當前位置
        node->left =  remove(node->left, key);
        return node;
  }

  else if(key > node->key) {
        node->right = remove(node->right, key);
        return node;
  }

    //這裡就找到了需要delete的node
  else {   //key == node->key)

 // 待刪除節點左子樹為空的情況  
         if(node->left == NULL){
               Node* rightNode = node->right;
              delete node;
              count--;
              return rightNode;
          }
        // 待刪除節點右子樹為空的情況
         if(node->right == NULL){
              Node* leftNode = node->left;
              delete node;
              count--;
              return leftNode;
          }
  }
  1. 情況二:刪除同時擁有左右孩子的節點
    如圖,我們現在要刪除圖中58節點,如果直接刪除58則41節點的右子樹就不再是在該二分搜尋樹中了
    二分搜尋樹系列之【 節點刪除 (remove) 】

所以,現在我們需要將59拿出來,作為41節點的右孩子(這裡只有59,53位置滿足條件)
二分搜尋樹系列之【 節點刪除 (remove) 】
繼續往下看:

二分搜尋樹系列之【 節點刪除 (remove) 】
這裡需要將原來58節點的右孩子變成59節點的右孩子
s->right = delMin(d->right)

二分搜尋樹系列之【 節點刪除 (remove) 】

s->right = delMin(d->right)就變成了下圖:
二分搜尋樹系列之【 節點刪除 (remove) 】

再將50節點變成59的左孩子
s->left = d->left:
二分搜尋樹系列之【 節點刪除 (remove) 】

最後將58節點刪除即可;利用遞迴將59節點返回給41節點(成為41節點的右孩子)。

下面看程式碼:
在public中定義:

//刪除二分搜尋樹中值的任意節點
void remove( Key key){
    root = remove(root, key);
}

在private中定義:

//刪除二分搜尋樹中值的任意節點
Node* remove(Node* node, Key key){
    //判斷node是否為空
  if(node == NULL) {
        return NULL;
  }

   //先找到需要刪除的值的node
  else if(key < node->key) {      //key為需要刪除的,node->key為當前位置
        node->left =  remove(node->left, key);
        return node;
  }

  else if(key > node->key) {
        node->right = remove(node->right, key);
        return node;
  }

    //這裡就找到了需要delete的node
  else {   //key == node->key)

 // 待刪除節點左子樹為空的情況  
         if(node->left == NULL){
               Node* rightNode = node->right;
              delete node;
              count--;
              return rightNode;
          }
        // 待刪除節點右子樹為空的情況
         if(node->right == NULL){
              Node* leftNode = node->left;
              delete node;
              count--;
              return leftNode;
          }
              // 待刪除節點左右子樹都不為為空的情況
         Node *succeer =new Node(minmum(node->right)); //找到最小key值的節點返回給succeer
         count ++;
         succeer->right = removeMin(node->right); //將最小key值的node刪除,並將返回值給succeer的右孩子

         succeer->left = node->left;
         delete node;
         count--;
         return succeer;
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章