樹及樹的演算法(1) —— 二叉樹

來源:互聯網
上載者:User

從現在開始,我們研究演算法理論中最重要的樹的實現問題,其中包括抽象樹、N叉樹及二叉樹,這裡重點研究二叉樹。樹狀圖是一種資料結構,它是由n(n>=1)個有限結點組成一個具有層次關係的集合。把它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點:每個結點有零個或多個子結點;每一個子結點只有一個父結點;沒有前驅的結點為根結點;除了根結點外,每個子結點可以分為m個不相交的子樹。

 

樹的總體架構:

 

1 二叉樹

二叉樹是樹的資料結構中,最重要,運用場合最多的技術,其總體架構如下:

 

首先來看抽象樹的實現,抽象樹是一個容器,各子樹的根作為其元素:

 

class Tree : public virtual Container{class Iter;public:virtual Object& Key () const = 0;virtual Tree& Subtree (unsigned int) const = 0;virtual bool IsEmpty () const = 0;virtual bool IsLeaf () const = 0;virtual unsigned int Degree () const = 0;virtual int Height () const;virtual void DepthFirstTraversal (PrePostVisitor& visitor) const;virtual void BreadthFirstTraversal (Visitor& visitor) const;Iterator& NewIterator() const;void Accept (Visitor& visitor) const;};

 

我們看到,抽象樹封裝了所有樹實現的通用元素:

函數Key:返回根節點對象的引用。

函數Subtree:返回給定樹的第i課子樹。

函數IsEmpty:判斷這棵樹是否是空樹。

函數IsLeaf:判斷這個節點是不是葉子節點。

函數Degree:返回根節點的度,即包含子樹的個數,葉子節點度為0。

函數Height:返回樹的高度。

函數DepthFirstTraversal:樹的深度優先遍曆。

函數BreadthFirstTraversal:樹的廣度優先遍曆。

 

2 樹的遍曆

(1)深度優先遍曆

 

//深度優先遍曆void Tree::DepthFirstTraversal( PrePostVisitor& visitor ) const{if (visitor.IsDone ()){return;}if (!IsEmpty ()){//前序走訪visitor.PreVisit (Key ());for (unsigned int i = 0; i < Degree (); ++i){//深度優先遍曆子樹Subtree(i).DepthFirstTraversal (visitor);}//後序遍曆visitor.PostVisit (Key ());}}

 

深度優先遍曆是遞迴地訪問根極其子樹方式來遍曆整個樹,演算法很簡單,舉例子:

如果是先序遍曆,深度優先遍曆首先訪問H,然後遞迴遍曆D和G,到D時又遞迴遍曆A和C,到C時又遞迴遍曆B,到G時又遞迴遍曆F,到F時又遞迴遍曆E。所以先序深度優先遍曆最後的結果是:HDACBGFE。

 

如果是後序遍曆,則首先遞迴遍曆D和G,再H;到D時又遞迴遍曆A、C再D;到C時又遞迴遍曆B,再C。到G時先F再G,到F時先E再F,最後後續深度優先遍曆的最後結果是:ABCDEFGH。

 

注意,這裡的抽象樹,還沒有到二叉樹的那個層級,所以暫不考慮中序遍曆。

 

我們的先序、中序和後續遍曆是通過訪問者模式來實現的:

class PuttingVisitor : public Visitor{ostream& stream;bool comma;public:PuttingVisitor (ostream& s) : stream (s), comma (false){}void Visit (Object& object){if (comma)stream << ", ";stream << object;comma = true;}    bool IsDone () const    {        return comma;    }};//遍曆基類class PrePostVisitor : public Visitor{public:virtual void PreVisit (Object&) {}virtual void Visit (Object&) {}virtual void PostVisit (Object&) {}};//前序走訪類class PreOrder : public PrePostVisitor{Visitor& visitor;public:PreOrder (Visitor& v) : visitor (v){}void PreVisit (Object& object){ visitor.Visit (object); }};//中序遍曆類class InOrder : public PrePostVisitor{Visitor& visitor;public:InOrder (Visitor& v) : visitor (v){}void Visit (Object& object){ visitor.Visit (object); }};//後序遍曆類class PostOrder : public PrePostVisitor{Visitor& visitor;public:PostOrder (Visitor& v) : visitor (v){}void PostVisit (Object& object){ visitor.Visit (object); }};

 

(2)廣度優先遍曆

 

再來看樹的廣度優先演算法,樹的廣度優先遍曆是按照樹的深度來訪問的,並在每一層上按照從左至右的順序訪問節點:

//廣度優先遍曆void Tree::BreadthFirstTraversal( Visitor& visitor ) const{Queue& queue = *new QueueAsLinkedList ();queue.RescindOwnership ();if (!IsEmpty ()){queue.Enqueue (const_cast<Tree&> (*this));}while (!queue.IsEmpty () && !visitor.IsDone ()){Tree const& head =dynamic_cast<Tree const &> (queue.Dequeue ());visitor.Visit (head.Key ());for (unsigned int i = 0; i < head.Degree (); ++i){Tree& child = head.Subtree (i);if (!child.IsEmpty ())queue.Enqueue (child);}}delete &queue;}

 

廣度優先演算法由於是按照高度來遍曆的,所以不能用遞迴的方式了。我們還是通過一個例子來研究這個演算法:

首先高度為3的H入隊列,進入迴圈,H出隊並接受訪問;隨後高度為2的D和G入隊;

到D時,高度為A和C的節點又入隊,D接受訪問;

到G時F入隊,G接受訪問;

到A時沒有入隊,A接受訪問;

到C時B入隊,C接受訪問;

到F時E入隊,F接受訪問;

到B時沒有入隊,B接受訪問;

到E時沒有入隊,E接受訪問。

最終廣度優先演算法是:HDGACFBE

 

(3)樹的迭代器

 

使用樹的迭代器也是一種遍曆的方法:

 

//樹的迭代器class Tree::Iter : public Iterator{Tree const& tree;Stack& stack;public:Iter (Tree const& _tree);~Iter ();void Reset ();bool IsDone () const;Object& operator * () const;void operator ++ ();};Tree::Iter::Iter( Tree const& _tree ):tree(_tree),stack (*new StackAsLinkedList ()){stack.RescindOwnership ();Reset ();}Tree::Iter::~Iter(){delete &stack; }void Tree::Iter::Reset(){stack.Purge ();if (!tree.IsEmpty ()){stack.Push (const_cast<Tree&> (tree));}}bool Tree::Iter::IsDone() const{return stack.IsEmpty ();}Object& Tree::Iter::operator*() const{if (!stack.IsEmpty ()){Tree const& top =dynamic_cast<Tree const&> (stack.Top ());return top.Key ();}else{return NullObject::Instance ();}}void Tree::Iter::operator++(){if (!stack.IsEmpty ()){Tree const& top =dynamic_cast<Tree const&> (stack.Pop ());for (int i = top.Degree () - 1; i >= 0; --i){Tree& subtree = top.Subtree (i);if (!subtree.IsEmpty ()){stack.Push (subtree);}}}}

 

我們主要關注++操作符,這個操作符號利用棧來實現子樹的遍曆。對於棧的運用,在這裡是一個很好的提現,希望好好學習這個思想。

3 二叉樹的實現

二叉樹是抽象樹Tree的衍生類別,但也是很多重要資料結構的基類,所以我們暫時不會實現一些通用的介面:

class BinaryTree : public virtual Tree{protected:Object* key;BinaryTree* left;BinaryTree* right;public:BinaryTree ();BinaryTree (Object& _key);void purge();~BinaryTree ();Object& Key () const;virtual BinaryTree& Left () const;virtual BinaryTree& Right () const;virtual void AttachLeft (BinaryTree&);virtual void AttachRight (BinaryTree&);virtual void AttachKey (Object&);virtual Object& DetachKey ();virtual BinaryTree& DetachLeft ();virtual BinaryTree& DetachRight ();// ...void DepthFirstTraversal (PrePostVisitor& visitor) const;};BinaryTree::BinaryTree():key(0),left(0),right(0){}BinaryTree::BinaryTree( Object& _key ):key(&_key)left(new BinaryTree()),right(new BinaryTree()){}void BinaryTree::purge(){if (!IsEmpty ()){if (IsOwner ())delete key;delete left;delete right;key = 0;left = 0;right = 0;}}BinaryTree::~BinaryTree(){purge();}//二叉樹的遍曆,採用先序方式void BinaryTree::DepthFirstTraversal( PrePostVisitor& visitor ) const{if (visitor.IsDone ()){return;}if (!IsEmpty ()){visitor.PreVisit (*key);left->DepthFirstTraversal (visitor);visitor.Visit (*key);right->DepthFirstTraversal (visitor);visitor.PostVisit (*key);}}

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.