用 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 { unsignedint 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) { unsignedint 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++,喜歡研究用加強的演算法和資料結構來提高基於人工智慧的系統。
[ 轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]