從二叉樹和iterator看程式碼結構設計 (關於adapter的運用) (轉)
前段時間,論壇上幾個人很熱烈地討論二叉樹裡做iterator。還有人專為此給一些C++界的名人寫信,儼然很專業的樣子。
老實說,我當時看了這個論題,感覺有點無聊,為什麼?因為二叉樹就是二叉樹,它的結構就是:左兒子,右兒子,父節點等。
iterator的結構是線性的前驅,後繼。
兩者雖然不是風馬牛不相及,但也應該是互相獨立的概念。
要給二叉樹做一個iterator不是不能,只要定義一個遍歷規則,把二叉樹以iterator的方式來訪問完全是可能的。
但是,這種可能就象拿stl可以做 programming一樣,但stl和Windows programming卻又是互相獨立的。 誰也不需要依賴誰。你要非在STL里加入windows sdk的東西,然後說:因為windows programming很常用,所以為了方便,我把HWND加入stl, 就亂套了。
把iterator和二叉樹的實現耦合起來考慮,在我看來,是明顯的設計問題。無謂地把簡單的東西複雜化。雖然可以做,但是寫出來的程式碼重用性和結構上都有毛病。
好了,毛病挑完了。下面該說說我認為正確的做法:
1。二叉樹就是二叉樹, 老老實實地把二叉樹的功能做好。不要考慮iterator.
2。 如果認為需要iterator, 如前序,中序,後序。別急。可以單獨做一個基於二叉樹的iterator的adapter. 這個adapter獨立於任何的二叉樹實現。它做的只是把二叉樹對映成線性訪問的iterator.
這個adapter自己定義一個二叉樹的介面。然後只需要把任何滿足這個介面的對映成iterator就成了。
3. 對一個二叉樹的實現,怎麼利用這個iterator的adapter呢?
把這個二叉樹先對映到這個adapter要求的介面。然後,把adapter往二叉樹上一套,iterator就出來了。這個iterator庫就象一個通用轉換器,往任何的二叉樹上一插,就行了。
這種結構的好處是,iterator不依賴於二叉樹的實現,二叉樹也不依賴於iterator. 一個二叉樹可以選用任何合適的adapter實現。而一個adapter也可以用於任何的一個二叉樹的實現。
下面是我剛剛的一段兩百多行的C++ bidirectional iterator adapter的程式碼。它可以用在任何的二叉樹實現上(當然,你得寫一個adapter把那個二叉樹對映到我的二叉樹介面上來,不要告訴我你不知道怎麼做)
namespace trit{
/*自己定義的語法,二叉樹的介面就是這樣:
template
protocol Tree{
NODE_PTR getLeft();
NODE_PTR getRight();
NODE_PTR getParent();
}這個是生成的iterator介面:
template
protocol Iterator{
operator bool();
NODE_PTR deref();
Self next();
Self prev();
}
*/
template
class Iterator{
protected:
typedef IteratorIt;
public:
NODE_PTR deref()const{
return node;
}
operator bool()const{
return node!=NODE_PTR(0);
}
Iterator(NODE_PTR const n):node(n){}
friend bool operator==(const It& a, const It& b){
return a.deref()==b.deref();
}
friend bool operator!=(const It& a, const It& b){
return !(a==b);
}
It& operator=(const It& other){
this->node = other.node;
return *this;
}
It end(){
return NODE_PTR(0);
}
static NODE_PTR get(NODE_PTR node){
NODE_PTR ret(node);
NODE_PTR tmp(ret->getParent());
for(;tmp!=NODE_PTR(0);ret=tmp, tmp=ret->getParent());
return ret;
}
static NODE_PTR getLeftmost(NODE_PTR node){
NODE_PTR ret(node);
NODE_PTR tmp(ret->getLeft());
for(;!isNull(tmp);ret=tmp, tmp=ret->getLeft());
return ret;
}
static NODE_PTR getLeftDeep(NODE_PTR node){
NODE_PTR ret(node);
for(;;){
NODE_PTR tmpl(ret->getLeft());
if(isNull(tmpl)){
NODE_PTR tmpr(ret->getRight());
if(isNull(tmpr)){
return ret;
}
else{
ret = tmpr;
}
}
else{
ret = tmpl;
}
}
}
static bool isNull(NODE_PTR ptr){
return ptr==NODE_PTR(0);
}
private:
NODE_PTR node;
};template
class Preorder;template
class Postorder;
template
class Inorder;template
class Preorder /*supports Iterator*/: public Iterator { typedef Preorder
Self;
public:
Self next()const{
return goLeft(deref());
}
Self prev()const{
typedef RevTreeAdapter;
Adapter rt(deref());
Postorderit(rt);
return Self(it.next().deref().getAdaptee());
}
Self begin()const{
return Self(getRoot(deref()));
}
private:static Self ret(NODE_PTR me, NODE_PTR from){
if(isNull(me)){
return Self(NODE_PTR(0));
}
else if(from==me->getLeft()){
return goRight(me);
}
else{
return goParent(me);
}
}
static Self goLeft(NODE_PTR const node){
NODE_PTR const left(node->getLeft());
if(isNull(left)){
return goRight(node);
}
else{
return Self(left);
}
}
static Self goRight(NODE_PTR const node){
NODE_PTR const right(node->getRight());
if(isNull(right)){
return goParent(node);
}
else{
return Self(right);
}
}
static Self goParent(NODE_PTR const node){
return ret(node->getParent(), node);
}
public:
Preorder(NODE_PTR const n):It(n){}
Self& operator=(const It& other){
return (Self&) It::operator=(other);
}
};template
class Inorder: public Iterator{
typedef InorderSelf;
public:
Self next()const{
return goRight(deref());
}
Self prev()const{
typedef RevTreeAdapter;
Adapter rt(deref());
Inorderit(rt);
return Self(it.next().deref().getAdaptee());
}
Self begin()const{
return Self(getLeftmost(deref()));
}
private:
static Self ret(NODE_PTR const me, NODE_PTR const from){
if(isNull(me)){
return Self(NODE_PTR(0));
}
else if(me->getLeft()==from){
return Self(me);
}
else{
return goParent(me);
}
}static Self goRight(NODE_PTR const node){
NODE_PTR const right(node->getRight());
if(isNull(right)){
return goParent(node);
}
else{
return Self(getLeftmost(right));
}
}
static Self goParent(NODE_PTR const node){
return ret(node->getParent(), node);
}
public:
Inorder(NODE_PTR const n):It(n){}
Self& operator=(const It& other){
return (Self&) It::operator=(other);
}
};template
class Postorder: public Iterator{
typedef PostorderSelf;
public:
Self next(){
return goParent(deref());
}
Self prev(){
typedef RevTreeAdapter;
Adapter rt(deref());
Preorderit(rt);
return Self(it.next().deref().getAdaptee());
}
Self begin(){
return Self(getLeftDeep(deref()));
}
static Self ret(NODE_PTR const me, NODE_PTR const from){
if(isNull(me)){
return Self(NODE_PTR(0));
}
if(me->getLeft()==from){
return goRight(me);
}
else{
return Self(me);
}
}
private:
static Self goRight(NODE_PTR const node){
NODE_PTR const right(node->getRight());
if(isNull(right)){
return Self(node);
}
else{
return Self(getLeftDeep(right));
}
}
static Self goParent(NODE_PTR const node){
return ret(node->getParent(), node);
}
public:
Postorder(NODE_PTR const n):It(n){}
Self& operator=(const It& other){
return (Self&) It::operator=(other);
}};
template
class RevTree/*supports Tree*/{
typedef RevTreeSelf;
public:
RevTree(NODE_PTR const t):tr(t){}
Self getLeft()const{
return Self(tr->getRight());
}
Self getRight()const{
return Self(tr->getLeft());
}
Self getParent()const{
return Self(tr->getParent());
}
const Self* operator->()const {return this;}bool operator == (const Self& b)const{
return this->tr==b.tr;
}
bool operator== (const NODE_PTR b){
return this->tr==b;
}
friend bool operator== (const NODE_PTR a, const Self& b){
return a==b.tr;
}
Self& operator=(const Self& other){
tr = other.tr;
return *this;
}NODE_PTR getAdaptee()const{return tr;}
private:
NODE_PTR tr;
};}
下面是測試程式碼,因為這裡的重點不是如何實現二叉樹, 它使用了一個非常簡易的二叉樹。只是為了表示你怎麼樣給一個任意的二叉樹提供iterator服務。
#include
#include "trit.h"
using namespace trit;
class TreeCons{
public:
TreeCons* getLeft()const{return left;}
TreeCons* getRight()const{return right;}
TreeCons* getParent()const{return parent;}
void setParent(TreeCons* p){this->parent = p;}
const char* get(){return val;}
TreeCons(TreeCons* l, TreeCons* r, const char* v)
:left(l), right(r), parent(0), val(v){}
private:
TreeCons* left;
TreeCons* right;
TreeCons* parent;
const char* val;
};
typedef Preorder
typedef Inorder
typedef Postorder
template
void printIt(IT it){
for(;it; it=it.next()){
cout << it.deref()->get();
}
cout <
template
void printRev(IT it){
for(;it; it=it.prev()){
cout << it.deref()->get();
}
cout <
int main(){
TreeCons* d = new TreeCons(0, 0, "D");
TreeCons* e = new TreeCons(0, 0, "E");
TreeCons* b = new TreeCons(d, e, "B");
d->setParent(b);
e->setParent(b);
TreeCons* f = new TreeCons(0, 0, "F");
TreeCons* c = new TreeCons(0, f, "C");
f->setParent(c);
TreeCons* a = new TreeCons(b, c, "A");
b->setParent(a);
c->setParent(a);
printIt(Pre(a));
printIt(Pre(c).begin());
printIt(In(a));
printIt(In(a).begin());
printIt(Post(a));
printIt(Post(a).begin());
printRev(Post(a));
printRev(In(a));
printRev(Pre(a));
delete a;
delete b;
delete c;
delete d;
delete e;
return 1;
}
也許你會覺得,這個iterator和stl的iterator的介面不大一致。呵呵,這個實現本著最小功能原則,其它的如operator++什麼的,自己寫個小adapter把介面轉換一下應該不難吧?
:〉
另外,我還寫了一個版本的binary tree -- iterator 的adapter. 利用java的gc功能, 它不需要節點有父指標。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-998868/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- [資料結構] 樹、二叉樹、森林的轉換資料結構二叉樹
- 二叉樹的子樹和子結構 c++二叉樹C++
- 從學生到程式設計師(一) 關於程式設計 (轉)程式設計師
- 從OKHttp框架看程式碼設計HTTP框架
- 關於二叉樹二叉樹
- 關於 Go 程式碼結構的思考Go
- 二叉樹的子結構二叉樹
- 運用關卡結構圖分析《曠野之息》的關卡設計
- 二叉連結串列儲存結構、二叉樹相關操作二叉樹
- 重學資料結構之樹和二叉樹資料結構二叉樹
- 重學資料結構(六、樹和二叉樹)資料結構二叉樹
- 關於elementUI樹狀結構的bugUI
- 資料結構(樹):二叉樹資料結構二叉樹
- 資料結構:樹和二叉樹定義和術語資料結構二叉樹
- 【資料結構虛擬碼】設計判斷一棵二叉樹是否是二叉排序樹的演算法資料結構二叉樹排序演算法
- 二叉樹的子結構、深度以及重建二叉樹二叉樹
- 資料結構中的樹(二叉樹、二叉搜尋樹、AVL樹)資料結構二叉樹
- 【資料結構導論之樹和二叉樹總結篇】資料結構二叉樹
- Java 樹結構實際應用 四(平衡二叉樹/AVL樹)Java二叉樹
- 關於Java併發程式設計的總結和思考Java程式設計
- 二叉樹的儲存結構二叉樹
- 二叉搜尋樹的結構
- 資料結構:二叉查詢樹的相關操作資料結構
- 團體程式設計天梯賽-玩轉二叉樹(簡潔建樹)程式設計二叉樹
- MVC 設計模式帶來更好的軟體結構和程式碼重用(轉)MVC設計模式
- 資料結構之樹結構概述(含滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹、紅黑樹、B-樹、B+樹、B*樹)資料結構二叉樹
- 資料結構和演算法:二叉樹資料結構演算法二叉樹
- 資料結構-二叉樹資料結構二叉樹
- 資料結構 - 二叉樹資料結構二叉樹
- 【資料結構】二叉樹!!!資料結構二叉樹
- 關於螢幕程式設計(轉)程式設計
- java資料結構和演算法——平衡二叉樹之左旋轉Java資料結構演算法二叉樹
- 從學生到程式設計師(三) 關於學習 (轉)程式設計師
- 資料結構學習(c++)——二叉樹 (轉)資料結構C++二叉樹
- 資料結構——樹與二叉樹的遍歷資料結構二叉樹
- reusable:前端可複用程式碼目錄結構的設計前端
- Java關於資料結構的實現:樹Java資料結構
- 關於一個樹狀結構的通用類