从二叉树和iterator看代码结构设计 (关于adapter的运用)

原创 2003年04月08日 14:25:00

前段时间,论坛上几个人很热烈地讨论二叉树里做iterator。还有人专为此给一些C++界的名人写信,俨然很专业的样子。

老实说,我当时看了这个论题,感觉有点无聊,为什么?因为二叉树就是二叉树,它的结构就是:左儿子,右儿子,父节点等。

iterator的结构是线性的前驱,后继。

两者虽然不是风马牛不相及,但也应该是互相独立的概念。

要给二叉树做一个iterator不是不能,只要定义一个遍历规则,把二叉树以iterator的方式来访问完全是可能的。

但是,这种可能就象拿stl可以做Windows 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<typename NODE_PTR>
protocol Tree{
 NODE_PTR getLeft();
 NODE_PTR getRight();
 NODE_PTR getParent();
}

这个是生成的iterator接口:
template<typename NODE_PTR>
protocol Iterator{
 operator bool();
 NODE_PTR deref();
 Self next();
 Self prev();
}
*/

 

template<typename NODE_PTR>
class Iterator{
protected:
 typedef Iterator<NODE_PTR> It;
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 getRoot(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<typename NODE_PTR>
class Preorder;

template<typename NODE_PTR>
class Postorder;
template<typename NODE_PTR>
class Inorder;

template<typename NODE_PTR>
class Preorder /*supports Iterator<NODE_PTR>*/: public Iterator<NODE_PTR>{

 typedef Preorder<NODE_PTR> Self;
public:
 Self next()const{
  return goLeft(deref());
 }
 Self prev()const{
  typedef RevTree<NODE_PTR> Adapter;
  Adapter rt(deref());
  Postorder<Adapter> it(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<typename NODE_PTR>
class Inorder: public Iterator<NODE_PTR>{
 typedef Inorder<NODE_PTR> Self;
public:
 Self next()const{
  return goRight(deref());
 }
 Self prev()const{
  typedef RevTree<NODE_PTR> Adapter;
  Adapter rt(deref());
  Inorder<Adapter> it(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<typename NODE_PTR>
class Postorder: public Iterator<NODE_PTR>{
typedef Postorder<NODE_PTR> Self;
public:
 Self next(){
  return goParent(deref());
 }
 Self prev(){
  typedef RevTree<NODE_PTR> Adapter;
  Adapter rt(deref());
  Preorder<Adapter> it(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<typename NODE_PTR>
class RevTree/*supports Tree<NODE_PTR>*/{
 typedef RevTree<NODE_PTR> Self;
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 <iostream.h>
#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<TreeCons*> Pre;
typedef Inorder<TreeCons*> In;
typedef Postorder<TreeCons*> Post;

template<typename IT>
void printIt(IT it){
 for(;it; it=it.next()){
  cout << it.deref()->get();
 }
 cout <<endl;
}
template<typename IT>
void printRev(IT it){
 for(;it; it=it.prev()){
  cout << it.deref()->get();
 }
 cout <<endl;
}
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把接口转换一下应该不难吧?

:〉

 

另外,我还写了一个Java版本的binary tree -- iterator 的adapter. 利用java的gc功能, 它不需要节点有父指针。

 

二叉树的各种概念以及代码操作

二叉树的各种概念以及代码操作一、基本概念 * 每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒。 性质: 1. 非空二叉树的第n层上至多有2^(n-1)个元素。 2. 深...
  • wgyscsf
  • wgyscsf
  • 2017年03月18日 12:26
  • 865

《代码大全》学习笔记(7):高级结构设计

7.1 软件设计引论        “软件设计”一词的意思是指,把一个计算机程序的定义转变成可以运行程序的设计方法;设计是联系要求定义和编码与调试的活动的桥梁;它是一个启发的过程而不是一个确定的过程...
  • zhouzxi
  • zhouzxi
  • 2013年04月10日 20:32
  • 2101

数据结构 《22》---- 二叉树三种遍历的迭代器算法

二叉树的三种遍历有递归版本,和迭代版本。本文介绍一种新的思路。 参考了 http://coolshell.cn/articles/9886.html 在许多应用中,我们还需要对遍历本身进行抽象...
  • u012653791
  • u012653791
  • 2014年07月07日 15:59
  • 1936

替代B树成为一些Nosql采用的数据结构

http://imtinx.iteye.com/blog/1291165 http://blog.nosqlfan.com/html/3132.html 在数据库优化中,索引优化是非常重要也...
  • wkkzmm126
  • wkkzmm126
  • 2014年03月31日 08:43
  • 696

必懂的公式定理--二叉树的性质

1、一般二叉树的性质 性质1、在非空二叉树的i层上,至多有2^i个结点。 性质2、高度为K的二叉树中,最多有2^(k+1)-1个结点。 性质3、对于任何一棵非空的二叉树,...
  • u013564742
  • u013564742
  • 2015年10月31日 19:32
  • 2890

java代码实现二叉树的遍历

一、二叉树的定义: 二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重...
  • qq_33275597
  • qq_33275597
  • 2016年10月08日 17:56
  • 1561

二叉树的五道面试题

1、判断一棵树是否是完全二叉树; 2、求二叉树中最远两个结点的距离; 3、由前序和中序遍历序列重建二叉树 (前序序列:1 2 3 4 5 6 - 中序序列:3 2 4 1 6 5); 4、求二叉...
  • LLZK_
  • LLZK_
  • 2016年11月30日 10:14
  • 881

请编写一个程序,实现将树转化成二叉树。(华为)

#include "stdafx.h" #include #include using namespace std; //树的节点 struct TreeNode { char e...
  • wangyangkobe
  • wangyangkobe
  • 2011年03月07日 16:16
  • 5831

从“关于Java堆与栈的思考”一帖看错误信息的传播

    我对转贴的信息一直有敌意,原因如下:首先,除了制造更多的信息垃圾,转贴不会带来新的价值,想收藏的话一个链接足矣;其次,将错误信息以讹传讹,混淆视听。不妨选一个典型的例子说明一二。    相信《...
  • yanghaishengking
  • yanghaishengking
  • 2010年12月03日 20:22
  • 212

二叉树的基本用法总结

下面列举了二叉树的一些常用方法,水平有限 代码写的质量不怎么地,基本功能是实现了的 功能有:树的建立、删除; 先序、后序、中序、层次遍历; 节点的查找,节点的插入; 兄弟的查找,父节点的查找; 计算树...
  • u011131874
  • u011131874
  • 2016年09月11日 11:08
  • 795
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:从二叉树和iterator看代码结构设计 (关于adapter的运用)
举报原因:
原因补充:

(最多只允许输入30个字)