C++樹結構實現中,為什麼要單獨定義節點類?
學習數據結構有關二叉樹的實現時,書上大概是這麼寫的:
裡面增加了節點類,感覺沒有必要,直接用下面的實現也可以吧?
class BTnode{
private:
string val;
BTnode * _lchild;
BTnode * _rchild;
}
class BinaryTree{
private:
BTnode *_root;
}
為什麼要單獨設置一個節點類呢?
class BinaryTree{
private:
string val;
BinaryTree*_root;
BinaryTree* _lchild;
BinaryTree* _rchild;
}
當tree和node還沒有加進其他函數的時候,的確你這兩種寫法是沒有區別的。不過到了stl裡面,你要搞begin和end的時候,還得分開寫。
很多oj都是以root指針的形式給你一棵樹。所以說不封裝也能玩。再比如說迭代器,樹的迭代器就是一個用棧實現的前序遍歷,前序遍歷給你root就能寫,同樣迭代器給你一個root指針就能生成。
把樹封裝的好處是不讓別人以任意形式操作你的樹,從而只可以以你提供的方法來操作。
如果你實在不想封裝,寫成 Type operation(Node *root) 也是沒問題的。因為對於一棵樹root只有一個。你的後一種實現給一顆樹的每個Node都添加了root域,不僅邏輯雜糅,而且浪費空間。
簡單的說,邏輯上的每個抽象定義都有各自的界限
一物多用,看似簡潔,但別人不好理解啊
個人覺得這樣就夠了。
class BinaryTree{
private:
string val;
BinaryTree* _lchild;
BinaryTree* _rchild;
}
其實最簡單只用如下定義就行(即只保留BTnode):
class BTnode{
private:
string val;
BTnode * _lchild;
BTnode * _rchild;
}
對於「實現」一棵簡單的二叉樹代碼,顯然兩種都是可以的。但是對於程序,同時要考慮可讀性。
如程序有兩處定義,一處定義是遍歷子樹void traverse(BTnode* node);
//此上省略諾干行代碼,假裝看不見上面
BTnode* t1, t2;
(如上例子同時適用於樓主的只有一個BinaryTree定義)
那麼再次讀這段代碼的時候,我並不能知道上述代碼的t1, t2, node這3個變數/參數分別指的到底是一棵樹還是一個節點,需要去查看具體的實現,在代碼量大的時候這個工作會更大。
這是原因之一,且對於教材來說,示例代碼的可讀性肯定是放在第一位的。當然,我們可以用規範的命名來解決上述問題。
而此外,當我們是在做一項工程的時候,同時要考慮到可擴展性。對於「結點」和「樹」是兩個不同的定義,是不能定義為同一個類的。
在此後使用這段代碼的時候,我發現我需要大量查詢某棵二叉樹的高度,那麼如果還用這樣的定義,就會導致每次查詢都需要O(n)的時間。
如果分開定義,那麼我只要在BinaryTree中加上一個屬性如下:class BinaryTree{
private:
int height;
BTnode *_root;
}
推薦閱讀: