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;
}

這樣height成為BinaryTree的一個屬性,只需要計算一次,這也是可擴展性的一個簡單實例,而對於節點,並沒有節點高度的定義,所以在節點class里加height並不合適。

當然等你加上成員方法的時候會更多的發現這些問題的,比如一些方法只能是樹的方法而不能是節點的方法。


推薦閱讀:

TAG:C | 數據結構 | 二叉樹 |