LATEST

Xóa node khỏi cây nhị phân tìm kiếm

Trong bài viết này, codehow sẽ hướng dẫn các bạn cách xóa node khỏi cây nhị phân tìm kiếm. Để hiểu được bài viết này, các bạn cần nắm vững kiến thức về cây nhị phân tìm kiếm. Biết được như thế nào là node con và node lá nhé.

Mình sẽ lần lượt hướng dẫn các bạn cách xóa node có một node con, node có hai node con và node lá trong cây nhị phân tìm kiếm. Sau đó mình sẽ thực hiện một chương trình bằng C++ để các bạn tham khảo nhé.

Bây giờ thì bắt đầu cùng mình thôi nào.

Cách xóa node khỏi cây nhị phân tìm kiếm

Việc thực hiện xóa node khỏi cây nhị phân chúng ta chia thành hai trường hợp như sau:

  • Xóa node có một node con và node lá.
  • Xóa node có hai node con.

Vì sao mình lại chia thành hai trường hợp như vậy thì các bạn hãy cùng mình tìm hiểu sẽ rõ nhé.

Trường hợp 1: Xóa node có một node con và node lá

Sau đây là các bước để thực hiện việc xóa node chứa một node con và node lá.

bai44 01 png

Bước 1: Kiểm tra xem cây nhị phân tìm kiếm có tồn tại phần tử hay không. Nếu không thì return rồi kết thúc hàm, ngược lại thì thực hiện bước tiếp theo.

Bước 2: So sánh node cần xóa với giá trị của node gốc. Nếu nhỏ hơn thì duyệt sang cây con bên trái để tìm, nếu lớn hơn thì duyệt sang cây con bên phải để tìm.

Bước 3: Sau khi tìm được node cần xóa, ta cập nhật lại mối liên kết trước khi xóa node. Tạo một node X bằng node t (X là node thế mạng, lát nữa ta sẽ xóa nó). Đối với trường hợp node có một node con thì ta chia thành hai tường hợp.

  • Node con nằm bên trái: Duyệt sang trái để cập nhật lại mối liên kết giữa node cha và node con của node cần xóa.
  • Node con nằm bên phải: Duyệt sang phải để cập nhật lại mối liên kết giữa node cha và node con của node cần xóa.

Bước 4: Sau khi đã cập nhật lại mối liên kết, bây giờ chúng ta đã có thể xóa node được rồi.

*Lưu ý: Hệ quả của việc xóa node có một node con chính là xóa luôn node lá. Bởi vì node lá là node không có cây con bên trái và cây con bên phải, vậy nên khi cập nhật mối liên kết thì cả bên trái và bên phải đều bằng NULL.

void XoaNode(TREE &t, int data) // data chính là giá trị của cái node cần xóa
{
    // nếu như cây đang rỗng
    if (t == NULL)
    {
        return; // kết thúc hàm
    }
    else
    {
        // nếu như data nhỏ hơn node gốc
        if (data < t->data)
        {
            XoaNode(t->pLeft, data); // duyệt sang nhánh trái của cây 
        }
        else if (data > t->data)
        {
            XoaNode(t->pRight, data); // duyệt sang nhánh phải của cây 
        }
        else // data == t->data - đã tìm ra cái node cần xóa
        {
            NODE *X = t; // node X là node thế mạng - tí nữa chúng ta sẽ xóa nó
            // nếu như nhánh trái bằng NULL <=> đây là cây có 1 con chính là con phải
            if (t->pLeft == NULL)
            {
                // duyệt sang phải của cái node cần xóa để cập nhật mối liên kết giữa node 
                // cha của node cần xóa với node con của node cần xóa
                t = t->pRight; 
            }
            else if (t->pRight == NULL)
            {
                // duyệt sang trái của cái node cần xóa để cập nhật mối liên kết giữa node 
                // cha của node cần xóa với node con của node cần xóa
                t = t->pLeft;
            }
            delete X; // xóa node cần xóa
        }
    }
}

Trường hợp 2: Xóa node có hai node con

Đối với trường hợp này thì phức tạp hơn rất nhiều so với trường hợp thứ nhất. Tuy nhiên nếu các bạn thực hiện theo từng bước, thì cũng không khó lắm đâu nhé.

Bước 1: Tìm node thế mạng cho node cần xóa. Vì khi chúng ta xóa node X đi, thì cần tìm một node khác để thế vào node X mà vẫn giữ nguyên tính chất của cây nhị phân. Để tìm được node thế mạng mình sẽ tạo hàm tìm NodeTheMang().

Có hai cách để tìm node thế mạng đó là:

  • Tìm node phải nhất bên cây con trái.
  • Tìm node trái nhất bên cây con phải.

Ở đây mình sẽ chọn cách tìm node trái nhất bên cây con phải.

Bước 2: Cập nhật lại dữ liệu của node cần xóa chính là node thế mạng. Cùng với đó là cập nhật lại mối liên kết của nó.

Bước 3: Ở hàm xóa node, ngoài hai trường hợp xóa node có một node con và node lá, ta thêm trường hợp xóa node có hai node con. Sau đó gọi đệ quy sang bên phải.

Dưới đây là hàm xóa node bất kì được nhập bởi người dùng.

// hàm tìm node thế mạng
void NodeTheMang(TREE &X, TREE &Y) // NODE Y là node thế mạng cho node cần xóa - node này sẽ đảm nhận nhiệm vụ tìm ra node trái nhất(TÌM NODE TRÁI NHẤT CÂY CON PHẢI) hoặc phải nhất(TÌM NODE PHẢI NHẤT CỦA CÂY CON TRÁI)
{
    // CÁCH 1: TÌM NODE TRÁI NHẤT CỦA CÂY CON PHẢI
    // duyệt sang bên trái nhất
    if (Y->pLeft != NULL)
    {
        NodeTheMang(X, Y->pLeft);// tìm ra node trái nhất ?
    }
    else // tìm ra được node trái nhất rồi nek..
    {
        X->data = Y->data; // cập nhật cái data của node cần xóa chính là data của node thế mạng
        X = Y; // cho node X(là node mà chúng ta sẽ đi xóa sau này) trỏ đến node thế mạng ==> ra khỏi hàm thì ta sẽ xóa node X
        Y = Y->pRight; // bản chất chỗ này chính là cập nhật lại mối liên kết cho node cha của node thế mạng(mà chúng ta sẽ xóa) với node con của node thế mạng  
    }
 
    /* CÁCH 2: TÌM NODE PHẢI NHẤT CỦA CÂY CON TRÁI
     duyệt sang bên phải
     if (Y->pRight != NULL)
    {
        DiTimNodeTheMang(X, Y->pRight);// tìm ra node phải nhất ?
    }
    else // tìm ra được node phải nhất rồi nek..
    {
        X->data = Y->data; // cập nhật cái data của node cần xóa chính là data của node thế mạng
        X = Y; // cho node X(là node mà chúng ta sẽ đi xóa sau này) trỏ đến node thế mạng ==> ra khỏi hàm thì ta sẽ xóa node X
        Y = Y->pLeft; // bản chất chỗ này chính là cập nhật lại mối liên kết cho node cha của node thế mạng(mà chúng ta sẽ xóa) với node con của node thế mạng   
    } */
}
 
// hàm xóa 1 cái node bất kì trong cây nhị phân tìm kiếm
void XoaNode(TREE &t, int data) // data chính là giá trị của cái node cần xóa
{
    // nếu như cây đang rỗng
    if (t == NULL)
    {
        return; // kết thúc hàm
    }
    else
    {
        // nếu như data nhỏ hơn node gốc
        if (data < t->data)
        {
            XoaNode(t->pLeft, data); // duyệt sang nhánh trái của cây 
        }
        else if (data > t->data)
        {
            XoaNode(t->pRight, data); // duyệt sang nhánh phải của cây 
        }
        else // data == t->data - đã tìm ra cái node cần xóa
        {
            NODE *X = t; // node X là node thế mạng - tí nữa chúng ta sẽ xóa nó
            // nếu như nhánh trái bằng NULL <=> đây là cây có 1 con chính là con phải
            if (t->pLeft == NULL)
            {
                // duyệt sang phải của cái node cần xóa để cập nhật mối liên kết giữa node 
                // cha của node cần xóa với node con của node cần xóa
                t = t->pRight; 
            }
            else if (t->pRight == NULL)
            {
                // duyệt sang trái của cái node cần xóa để cập nhật mối liên kết giữa node 
                // cha của node cần xóa với node con của node cần xóa
                t = t->pLeft;
            }
            else // node cần xóa là node có 2 con
            {
                // CÁCH 1: Tìm node trái nhất của cây con phải(cây con phải của cái node cần xóa)
                NodeTheMang(X, t->pRight);
                // CÁCH 2: Tìm node phải nhất của cây con trái(cây con trái của cái node cần xóa)
                //DiTimNodeTheMang(X, t->pLeft);
            }
            delete X; // xóa node cần xóa
        }
    }
}

Ví dụ xóa node khỏi cây nhị phân tìm kiếm

Trong phần này mình sẽ thực hiện một chương trình để xóa node khỏi cây nhị phân tìm kiếm. Node này sẽ do người dùng nhập từ bàn phím. Chương trình được viết bằng ngôn ngữ C++, trong chương trình mình có giải thích chi tiết, các bạn có thể tham khảo nhé.

#include<iostream>
using namespace std;
// khai báo cấu trúc 1 cái node trong cây nhị phân tìm kiếm
struct node
{
    int data; // dữ liệu chứa trong 1 cái node
    struct node *pLeft; // con trỏ liên kết với cái node bên trái <=> cây con trái
    struct node *pRight; // con trỏ liên kết với cái node bên phải <=> cây con phải
};
typedef struct node NODE;
typedef NODE* TREE;
  
// hàm khởi tạo cây nhị phân tìm kiếm
void KhoiTaoCay(TREE &t)
{
    t = NULL;
}
  
// hàm thêm 1 cái phần tử vào cây
void ThemNodeVaoCay(TREE &t, int x)
{
    // nếu cây rỗng
    if (t == NULL)
    {
        NODE *p = new NODE;
        p->data = x;
        p->pLeft = NULL;
        p->pRight = NULL;
        t = p;
    }
    else
    {
        // nếu phần tử thêm vào mà nhỏ hơn nút gốc thì sẽ duyệt qua bên trái
        if (x < t->data)
        {
            ThemNodeVaoCay(t->pLeft, x);
        }
        else if (x > t->data)
        {
            ThemNodeVaoCay(t->pRight, x);
        }
    }
}
  
// hàm xuất phần tử trong cây nhị phân tìm kiếm
void NLR(TREE t)
{
    if (t != NULL)
    {
        // xử lí
        cout << t->data << "  ";
        NLR(t->pLeft);
        NLR(t->pRight);
    }
}
  
 
// hàm tìm node thế mạng
void NodeTheMang(TREE &X, TREE &Y) // NODE Y là node thế mạng cho node cần xóa - node này sẽ đảm nhận nhiệm vụ tìm ra node trái nhất(TÌM NODE TRÁI NHẤT CÂY CON PHẢI) hoặc phải nhất(TÌM NODE PHẢI NHẤT CỦA CÂY CON TRÁI)
{
    // CÁCH 1: TÌM NODE TRÁI NHẤT CỦA CÂY CON PHẢI
    // duyệt sang bên trái nhất
    if (Y->pLeft != NULL)
    {
        NodeTheMang(X, Y->pLeft);// tìm ra node trái nhất ?
    }
    else // tìm ra được node trái nhất rồi nek..
    {
        X->data = Y->data; // cập nhật cái data của node cần xóa chính là data của node thế mạng
        X = Y; // cho node X(là node mà chúng ta sẽ đi xóa sau này) trỏ đến node thế mạng ==> ra khỏi hàm thì ta sẽ xóa node X
        Y = Y->pRight; // bản chất chỗ này chính là cập nhật lại mối liên kết cho node cha của node thế mạng(mà chúng ta sẽ xóa) với node con của node thế mạng  
    }
 
    /* CÁCH 2: TÌM NODE PHẢI NHẤT CỦA CÂY CON TRÁI
     duyệt sang bên phải
     if (Y->pRight != NULL)
    {
        DiTimNodeTheMang(X, Y->pRight);// tìm ra node phải nhất ?
    }
    else // tìm ra được node phải nhất rồi nek..
    {
        X->data = Y->data; // cập nhật cái data của node cần xóa chính là data của node thế mạng
        X = Y; // cho node X(là node mà chúng ta sẽ đi xóa sau này) trỏ đến node thế mạng ==> ra khỏi hàm thì ta sẽ xóa node X
        Y = Y->pLeft; // bản chất chỗ này chính là cập nhật lại mối liên kết cho node cha của node thế mạng(mà chúng ta sẽ xóa) với node con của node thế mạng   
    } */
}
 
// hàm xóa 1 cái node bất kì trong cây nhị phân tìm kiếm
void XoaNode(TREE &t, int data) // data chính là giá trị của cái node cần xóa
{
    // nếu như cây đang rỗng
    if (t == NULL)
    {
        return; // kết thúc hàm
    }
    else
    {
        // nếu như data nhỏ hơn node gốc
        if (data < t->data)
        {
            XoaNode(t->pLeft, data); // duyệt sang nhánh trái của cây 
        }
        else if (data > t->data)
        {
            XoaNode(t->pRight, data); // duyệt sang nhánh phải của cây 
        }
        else // data == t->data - đã tìm ra cái node cần xóa
        {
            NODE *X = t; // node X là node thế mạng - tí nữa chúng ta sẽ xóa nó
            // nếu như nhánh trái bằng NULL <=> đây là cây có 1 con chính là con phải
            if (t->pLeft == NULL)
            {
                // duyệt sang phải của cái node cần xóa để cập nhật mối liên kết giữa node 
                // cha của node cần xóa với node con của node cần xóa
                t = t->pRight; 
            }
            else if (t->pRight == NULL)
            {
                // duyệt sang trái của cái node cần xóa để cập nhật mối liên kết giữa node 
                // cha của node cần xóa với node con của node cần xóa
                t = t->pLeft;
            }
            else // node cần xóa là node có 2 con
            {
                // CÁCH 1: Tìm node trái nhất của cây con phải(cây con phải của cái node cần xóa)
                NodeTheMang(X, t->pRight);
                // CÁCH 2: Tìm node phải nhất của cây con trái(cây con trái của cái node cần xóa)
                //DiTimNodeTheMang(X, t->pLeft);
            }
            delete X; // xóa node cần xóa
        }
    }
}
  
// hàm nhập cây nhị phân tìm kiếm
void Menu(TREE &t)
{
    int luachon;
    while(true)
    {
        cout << "\n\n\t\t ============ MENU ============";
        cout << "\n1. Nhập phần tử cho cây";
        cout << "\n2. Duyệt cây";
        cout << "\n3. Xóa một Node bất kì";
        cout << "\n0. Thoát";
        cout << "\n\n\t\t =============  END  =============";
  
        cout << "\nNhập lựa chọn của bạn: ";
        cin >> luachon;
  
        if (luachon == 1)
        {
            int x;
            cout << "\nNhập giá trị: ";
            cin >> x;
            ThemNodeVaoCay(t, x);
        }
        else if (luachon == 2)
        {
            cout << "\nCây nhị phân tìm kiếm\n";
            NLR(t);
        }
        else if (luachon == 3)
            {
                  int x;
                cout << "\nNhập giá trị cần xóa: ";
                cin >> x;
                XoaNode(t, x);
            }
        else
        {
            break;
        }
    }
}
  
  
int main()
{
    TREE t;
    KhoiTaoCay(t);
    Menu(t);
  
    cout<<"\n---------------------------------\n";
  cout<<"Chương trình này được đăng tại codehow.net";
}

Kết quả trước khi xóa:

bai44 02 png

Kết quả sau khi xóa:

bai44 03 png

Cùng chuyên mục:

Tìm node Max và Min trong cây nhị phân tìm kiếm

Tìm node Max và Min trong cây nhị phân tìm kiếm

Xuất node con và node lá trong cây nhị phân tìm kiếm

Xuất node con và node lá trong cây nhị phân tìm kiếm

Tìm kiếm trên cây nhị phân tìm kiếm

Tìm kiếm trên cây nhị phân tìm kiếm

Duyệt cây nhị phân tìm kiếm

Duyệt cây nhị phân tìm kiếm

Thêm node vào cây nhị phân tìm kiếm

Thêm node vào cây nhị phân tìm kiếm

Cây nhị phân (Binary tree) là gì? Cơ chế hoạt động của nó

Cây nhị phân (Binary tree) là gì? Cơ chế hoạt động của nó

Cách gộp hai danh sách liên kết đôi

Cách gộp hai danh sách liên kết đôi

Tìm kiếm phần tử trong DSLK đôi

Tìm kiếm phần tử trong DSLK đôi

Xóa node trong DSLK đôi

Xóa node trong DSLK đôi

Chèn node (Insert node) vào DSLK đôi

Chèn node (Insert node) vào DSLK đôi

Duyệt danh sách liên kết đôi

Duyệt danh sách liên kết đôi

Tạo node mới trong DSLK đôi

Tạo node mới trong DSLK đôi

DSLK đôi là gì? Cấu trúc dữ liệu của DSLK đôi

DSLK đôi là gì? Cấu trúc dữ liệu của DSLK đôi

Quản lý sinh viên bằng DSLK đơn

Quản lý sinh viên bằng DSLK đơn

Tìm kiếm và sắp xếp trong DSLK đơn

Tìm kiếm và sắp xếp trong DSLK đơn

Xóa node (Delete node) trong DSLK đơn

Xóa node (Delete node) trong DSLK đơn

Chèn node (Insert node) vào DSLK đơn

Chèn node (Insert node) vào DSLK đơn

Tạo node mới trong DSLK đơn

Tạo node mới trong DSLK đơn

Cấu trúc dữ liệu của DSLK đơn

Cấu trúc dữ liệu của DSLK đơn

Danh sách liên kết (Linked List) là gì? Các loại danh sách liên kết

Danh sách liên kết (Linked List) là gì? Các loại danh sách liên kết

Top