1. 程式人生 > >C++實現二叉搜尋樹(遞迴&非遞迴)

C++實現二叉搜尋樹(遞迴&非遞迴)

二叉搜尋樹(Binary Search Tree)的性質:
1、每個節點都有一個作為搜尋關鍵碼的key,並且所有節點的key都不相同。
2、左子樹上的key值都小於根節點的key值。
3、右子樹上的key值都大於根節點的key值。
4、左右子樹都是二叉搜尋樹。
如下圖為一顆二叉搜尋樹
這裡寫圖片描述
實現:
二叉搜尋樹的插入:
二叉樹的插入是根據要插入節點的key值找一個合適的位置插入節點,如果要插入節點的key值大於當前結點的key值,則向右樹尋找合適的位置,如果要插入節點的key值小於當前結點的key值,則向左樹尋找合適的位置,如果要插入的節點的key值等於當前結點的key值,則說明該節點已存在,直接返回,不進行插入。
程式碼實現:

bool Insert(int key)//插入-非遞迴
    {
        Node * cur = _root;
        Node * parent = NULL;
        while(cur)
        {
            if(cur->_key < key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                parent
= cur; cur = cur->_left; } else //cur->_key==key 節點存在 { return false; } } cur = new Node(key); if (parent == NULL) { _root = cur; } else { if
(parent->_key > key) { parent->_left = cur; } else { parent->_right = cur; } } return true; } bool InsertR(int key)//插入-遞迴 { return _InsertR(_root, key); } bool _InsertR(Node *& root, int key)//插入-遞迴 { if (root == NULL) { root = new Node(key); return true; } else { if (root->_key > key) { return _InsertR(root->_left, key); } else if (root->_key < key) { return _InsertR(root->_right, key); } else//該數存在 { return false; } } }

二叉搜尋樹的查詢類似於二分查詢,如果要查詢節點的key值大於當前結點的key值,則向右樹查詢,如果要插入節點的key值小於當前結點的key值,則向左樹查詢,如果要插入的節點的key值等於當前結點的key值,則返回該節點。若走到一個空的位置還沒有該節點,則說明節點不存在。
程式碼實現:

Node* Find(int key)//查詢-非遞迴
    {
        Node *cur = _root;
        while (cur)
        {
            if (cur->_key < key)
            {
                cur = cur->_right;
            }
            else if(cur->_key>key)
            {
                cur = cur->_left;
            }
            else
            {
                return cur;
            }
        }
        return NULL;
    }
    Node * FindR(int key)//查詢-遞迴
    {
        return _FindR(_root, key);
    }
    Node* _FindR(Node *& root, int key)//查詢-遞迴
        {
            while (root)
            {
                if (root->_key == key)
                {
                    return root;
                }
                else if (root->_key > key)
                {
                    return _FindR(root->_left,key);
                }
                else
                {
                    return _FindR(root->_right, key);
                }
            }
            return NULL;    
        }

二叉搜尋樹的刪除分四種情況:刪除葉子結點、要刪除的節點無左孩子、要刪除的節點無右孩子、要刪除的節點既有左孩子也有右孩子。在實際操作中刪除葉子節點可以歸為要刪除的節點無左孩子或無右孩子裡面,因此,其實刪除就只有三種情況了,下面我畫圖來說明這三種刪除的處理方法。
這裡寫圖片描述
程式碼實現:

bool Remove(int key)//刪除-非遞迴
    {
        Node * cur = _root;
        Node * parent = NULL;
        Node *del = NULL;
        while (cur)
        {
            if (cur->_key < key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                if (cur->_left == NULL)//左為空or葉子結點
                {
                    del = cur;
                    if (cur == _root)//parent==NULL刪除根節點
                    {
                        _root = cur->_right;
                    }
                    else if (parent->_left==cur)
                    {
                        parent->_left = cur->_right;
                    }
                    else
                    {
                        parent->_right = cur->_right;
                    }
                }
                else if (cur->_right == NULL)//右為空
                {
                    del = cur;
                    if (cur == _root)//刪除根節點
                    {
                        _root = cur->_left;
                    }
                    else if (parent->_left == cur)
                    {
                        parent->_left = cur->_left;
                    }
                    else
                    {
                        parent->_right = cur->_left;
                    }
                }
                else//左右都不為空
                {
                    //找右孩子的最左節點替換該節點
                    Node * subLeft = cur->_right;
                    while (subLeft->_left)
                    {
                        parent = subLeft;
                        subLeft = subLeft->_left;
                    }
                    del = subLeft;
                    cur->_key = subLeft->_key;
                    if (subLeft == cur->_right)//cur->_right無左孩子
                    {
                        cur->_right = subLeft->_right;
                    }
                    else
                    {
                        parent->_left = subLeft->_right;
                    }
                }
                delete del;
                del = NULL;
                return true;
            }
        }
        return false;
    }
    bool RemoveR(int key)//刪除-遞迴
    {
        return _RemoveR(_root,key);
    }
    bool _RemoveR(Node * &root,int key)//刪除-遞迴
        {
            while (root)
            {
                if (root->_key > key)
                {
                    return _RemoveR(root->_right, key);
                }
                else if (root->_key < key)
                {
                    return _RemoveR(root->_left, key);
                }
                else
                {
                    Node *parent = NULL;
                    Node * del = NULL;
                    if (root->_left == NULL)//左為空or葉子結點
                    {
                        del = root;
                        if (root == _root)//parent==NULL刪除根節點
                        {
                            _root = root->_right;
                        }
                        else if (parent->_left == root)
                        {
                            parent->_left = root->_right;
                        }
                        else
                        {
                            parent->_right = root->_right;
                        }
                    }
                    else if (root->_right == NULL)//右為空
                    {
                        del = root;
                        if (root == _root)//刪除根節點
                        {
                            _root = root->_left;
                        }
                        else if (parent->_left == root)
                        {
                            parent->_left = root->_left;
                        }
                        else
                        {
                            parent->_right = root->_left;
                        }
                    }
                    else//左右都不為空
                    {
                        //找右孩子的最左節點替換該節點
                        Node * subLeft = root->_right;
                        while (subLeft->_left)
                        {
                            parent = subLeft;
                            subLeft = subLeft->_left;
                        }
                        del = subLeft;
                        root->_key = subLeft->_key;
                        if (subLeft == root->_right)//cur->_right無左孩子
                        {
                            root->_right = subLeft->_right;
                        }
                        else
                        {
                            parent->_left = subLeft->_right;
                        }
                    }
                    delete del;
                    del = NULL;
                    return true;
                }
            }
            return false;
        }