1. 程式人生 > >用 C++ 標準模板庫(STL)的 vector 實現二叉搜尋樹(BST)

用 C++ 標準模板庫(STL)的 vector 實現二叉搜尋樹(BST)

介紹

眾所周知,要建一棵樹,我們需要關注它的記憶體分配與釋放。為了避開這個問題,我打算用C++ STL(vector和deque)來建一棵小型的BST。很明顯,這篇文章是出於這個想法的。

背景

BST是應用最廣泛的資料結構之一。C是首選語言,不過因為C++尤其是C++11的出現,我更有興趣用C++來實現。但是這篇文章裡沒有涉及到C++11,程式碼可用C++98來編譯。

使用程式碼

要建BST,我們需要BST的資料結構。傳統的BST資料結構包含指向左右子樹的指標。我將用vector而不用指標,所以我將用vector的下標作為指向左右子樹的指標。

struct bst
{
    unsigned
int data; int leftIdx; int rightIdx; };

接下來,我會寫各種建BST和它的孩子結點的函式。第一個函式用來建BST。它將用傳入的資料來初始化資料結構,並且把左右下標置為-1(相當於置指標為NULL)。

void MakeNode(vector<struct> &v1, int aData)
{
    struct bst b1 = { aData, -1, -1 };
    v1.push_back(b1);
}

下面這個函式的功能是設定結點的左右孩子。設定時,會把左右孩子的真正下標(譯者注:相當於它們的地址)賦到根結點上。

void setleft(vector <struct>&v1, int currIndex, int aData)
{
    unsigned int leftIndex = v1.size();
    v1[currIndex].leftIdx = leftIndex;
    struct bst b1 = { aData, -1, -1 };
    v1.push_back(b1);
}

void setright(vector<struct> &v1, int currIndex, int aData)
{
    unsigned
int rightIndex = v1.size(); v1[currIndex].rightIdx = rightIndex; struct bst b1 = { aData, -1, -1 }; v1.push_back(b1); }

下面這個函式用於向BST插入資料。對所有結點(譯者注:不應是“所有”,平均時間複雜度應是O(logn))遍歷直至找到合適的位置插入元素,其中會呼叫上面定義的左右函式。

void Insert(vector<struct bst> &v1, int aData)
{
    if(v1.size() == 0)
    {
        cout<<"Note is not made yet. MakeNode first..."<<endl;
        return;
    }
    unsigned int currentIdx = 0;
    while ( currentIdx < v1.size() )
    {
        if(aData <= v1[currentIdx].data)
        {
            if( v1[currentIdx].leftIdx == -1)
            {
                setleft(v1, currentIdx, aData);
                break;
            }
            else
                currentIdx = v1[currentIdx].leftIdx;
        }
        else
        {
            if(v1[currentIdx].rightIdx == -1)
            {
                setright(v1, currentIdx, aData);
                break;
            }
            else
                currentIdx = v1[currentIdx].rightIdx;
        }
    }
}

下面的程式碼將以前序、中序、後序遍歷BST。下標引數是開始點。

void InTrav(vector <struct bst> &v1, unsigned int Idx)
{
    if(v1[Idx].leftIdx != -1)
        InTrav(v1,v1[Idx].leftIdx );
    cout<<v1[Idx].data<<endl;
    if( v1[Idx].rightIdx != -1)
        InTrav(v1, v1[Idx].rightIdx);
}

void PreTrav(vector <struct bst> &v1, unsigned int Idx)
{
    cout<<v1[Idx].data<<endl;
    if(v1[Idx].leftIdx != -1)
        PreTrav(v1,v1[Idx].leftIdx );
    if( v1[Idx].rightIdx != -1)
        PreTrav(v1, v1[Idx].rightIdx);
}
void PostTrav(vector <struct bst> &v1, unsigned int Idx)
{
    if(v1[Idx].leftIdx != -1)
        PostTrav(v1,v1[Idx].leftIdx );
    if( v1[Idx].rightIdx != -1)
        PostTrav(v1, v1[Idx].rightIdx);
    cout<<v1[Idx].data<<endl;
}

主程式比較簡單,如下

int main()
{
    vector <struct bst> v1;
    MakeNode(v1, 30);
    Insert(v1, 20);
    Insert(v1, 6);
    Insert(v1, 40);
    Insert(v1, 35);
    Insert(v1, 16);
    Insert(v1, 7);

    InTrav(v1, 0);
    PreTrav(v1,0);
    PostTrav(v1,0);
    return 0;
}

興趣點

1、同樣的程式碼也可用於STL deque。我還沒為任何其他容器測試。

2、與原生指標比起來,效率較低,除非做一些vector(STL)優化。我還沒去嘗試。

3、你可以用它來建小型的BST,可以一下子刪除它而不用擔心記憶體釋放。

4、對於BST的刪除元素操作,我會在下一篇帖子中介紹。

歷史

第一篇帖子

許可證

關於作者

架構師

印度

我熱愛程式設計,甚至在我接受專業教育之前就開始了(1995年)。從那以後,我一直從事於IP網路棧(寫IPv6棧和下一代TCP棧),VoIP,IP安全(IKE/IPSec)。

我最熱衷的程式語言是C++,喜歡研究用加強的演算法和資料結構來提高基於人工智慧的系統。

轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]