二叉樹 原

二叉樹和樹的根本區別

  • 二叉樹的每個元素都恰好有兩棵子樹(其中一個或兩個可能爲空)。而樹的每個元素可有任意數量的子樹。
  • 在二叉樹中,每個元素的子樹都是有序的。也就是說,有左子樹和右子樹之分。而樹的子樹是無序的.

二叉樹的特性

特性1 一棵二叉樹有n個元素,n>=0,它有n-1條邊。

證明 二叉樹的每個元素(除了根節點)有且僅有一個父節點。在子節點與父節點間有且只有一條邊,因此邊數爲n-1。


特性2 一棵二叉樹的高度爲h,h>=0,它最少有h個元素,最多有2^h-1個元素。

證明 二叉樹每一級最少有1個元素,因此元素的個數最少爲h。 每個元素最多有2個子節點,則第i層元素最多爲2^(i-1)個,i>0.則2^0+2^1+2^2+……+2^h=2^h-1.所以h層元素的總數最大爲2^h-1.


特性3 一棵二叉樹有n個元素,n>0,它的高度最大爲n,最小高度爲log2(n+1)向上取整。

證明 因爲每層至少有一個元素,因此高度不不會超過n。高度爲h的二叉樹最多有2^h-1個元素。因爲n<=2^h-1,所以,h>=log2(n+1)。由於h是整數,所以log2(n+1)要向上取整。及最小高度爲log2(n+1)向上取整。


當高度爲h的二叉樹恰好有2^h-1個元素時,稱其爲滿二叉樹。

對高度爲h的滿二叉樹的元素,從第一層到最後一層,在每一次中從左至右,順序編號,從1到2^h-1.假設從滿二叉樹中刪除k個其編號爲2^h-i元素,1<=i<=k<2^h,所得到的二叉樹被稱爲完全二叉樹

滿二叉樹是完全二叉樹的一個特例。


特性4 設完全二叉樹的某一元素編號爲i,1<=i<=n。有以下關係成立:

  • 如果i=1,則該元素爲二叉樹的根。若i>1,則其父親節點的編號爲i/2向下取整。
  • 如果2i>n,則該元素無左孩子。否則,其左孩子的編號爲2i
  • 如果2i+1>n,則該元素無右孩子。否則,其右孩子的編號爲2i+1.

二叉樹的常用操作

二叉樹的常用操作有:

  • 確定高度
  • 確定元素數目
  • 複製
  • 顯示或打印二叉樹
  • 確定兩棵二叉樹是否一樣
  • 刪除整棵樹

所有的這些操作都可以通過二叉樹的遍歷來完成。在二叉樹的遍歷中,每個元素僅被訪問一次。

二叉樹的遍歷

二叉樹的遍歷常用的有如下四種方法:

  • 前序遍歷
  • 中序遍歷
  • 後序遍歷
  • 層次遍歷

**二叉樹的中序遍歷和先序遍歷、中序遍歷和後序遍歷可以唯一確定一棵二叉樹。**除此之外,任意兩種遍歷方式結合都無法唯一確定一棵樹。

先序遍歷、中序遍歷、後序遍歷可以方便的使用棧來實現,所以可以使用遞歸實現。但是,層次遍歷的本質其實是隊列,所以用遞歸實現會比較困難。

二叉樹的描述

二叉樹可以用兩種方式來描述:數組鏈表

數組描述 將二叉樹中的元素從上到下、從左到右順序編號。

一個有n個元素的二叉樹可能最多需要2^n-1個空間來存儲。最壞的情況是,每層只有一個元素,n個元素的二叉樹就有n層。n層二叉樹存儲需要的空間個數即爲滿二叉樹的元素個數。 用數組來表示二叉樹的時候,空節點也是要存儲在數組中的,因爲數組不僅要存儲元素,還要存儲元素的相對關係。 二叉樹的數組表示如下:

鏈表描述 二叉樹最常用的表示方法是指針。每個元素用一個節點表示,節點有兩個指針域,分別稱爲leftChild(左孩子)和rightChild(右孩子).除此之外,還有一個用來element域,用來存儲當前節點的值。 n個節點,每個節點有2個指針,則一棵二叉樹共有2n個指針。父節點的每一個指向孩子的指針表示一條邊,則n個元素的二叉樹共有n-1條邊。所以,二叉樹一共會有2n-(n-1)=n+1個指針域沒有值,它們被置爲NULL。

如下圖,二叉樹的鏈表表示:

從根節點開始,沿着leftChild和rightChild指針域,可以訪問二叉樹的所有節點。二叉樹的鏈式表示沒有指向父節點的指針,因爲大部分函數不需要這樣的指針。若某些應用需要這種指針,可以在每個節點再添加一個指針域。

下面是二叉樹的具體代碼實現:

/*
 * 二叉樹遍歷操作
 *binaryTreeTraversals.cpp
*/
#include<iostream>
#include"arrayqueue.h"
#include"binarytreenode.h"
#include"myexceptions.h"

using namespace std;

//訪問二叉樹節點的元素
template <class T>
void visit(binaryTreeNode<T> *x)
{
    cout << x->element <<' ';
}

//先序遍歷
template <class T>
void preOrder(binaryTreeNode<T> *t)
{
    if( t != NULL)
    {
        visit(t);
        preOrder(t->leftChild);
        preOrder(t->rightChild);
    }
}

//中序遍歷
template <class T>
void inOrder(binaryTreeNode<T> *t)
{
    if(t != NULL)
    {
        inOrder(t->leftChild);
        visit(t);
        inOrder(t->rightChild);
    }
}

//後序遍歷
template <class T>
void postOrder(binaryTreeNode<T>* t)
{
    if(t != NULL)
    {
        postOrder(t->leftChild);
        postOrder(t->rightChild);
        visit(t);
    }
}

//層次遍歷
/*
 * 層次遍歷跟前面三種遍歷不同,用遞歸比較難實現,適合用隊列來實現
*/
template <class T>
void levelOrder(binaryTreeNode<T>* t)
{
    arrayQueue<binaryTreeNode<T>*> q;
    while( t != NULL)
    {
        visit(t);

        if(t->leftChild != NULL)//坐孩子入隊
            q.push(t->leftChild);
        if(t->rightChild != NULL)//右孩子入隊
            q.push((t->rightChild));

        try{ t = q.front();}//出隊操作
        catch(queueEmpty){return;}
        q.pop();
    }
}


int main(void)
{
    binaryTreeNode<int> *x,*y,*z;
    y = new binaryTreeNode<int> (2);
    z = new binaryTreeNode<int> (3);
    x = new binaryTreeNode<int> (1,y,z);

    //中序遍歷的輸出結果
    cout<<"Inorder sequence is ";
    inOrder(x);
    cout <<endl;

    //先序遍歷的結果
    cout<<"Preorder sequence is";
    preOrder(x);
    cout <<endl;

    //後序遍歷
    cout<<"Postorder sequence is";
    postOrder(x);
    cout<<endl;

    //層次遍歷
    cout<<"Level order sequence is";
    levelOrder(x);
    cout<<endl;

    cout <<"Hello World!"<<endl;
    return 0;

}
/*
 * 二叉樹節點定義
 * binaryTreeNode.h
*/
#ifndef BINARYTREENODE_H
#define BINARYTREENODE_H

#include<iostream>
using namespace std;

template <class T>
struct binaryTreeNode
{

    T element;
    binaryTreeNode<T> *leftChild,
                      *rightChild;

    binaryTreeNode()
    {
        leftChild = rightChild = NULL;
    }

    binaryTreeNode(const T& theElement):element(theElement)
    {
        leftChild = rightChild = NULL;
    }

    binaryTreeNode(const T& theElement,
                   binaryTreeNode *theLeftChild,
                   binaryTreeNode *theRightChild):element(theElement)
    {
        leftChild = theLeftChild;
        rightChild = theRightChild;
    }
};


#endif // BINARYTREENODE_H
/*
 * 二叉樹的定義
 * 二叉樹主要的操作是遍歷,這個在搜索、排序算法中很常用
 * binaryTree.h
 *
*/
#ifndef BINARYTREE_H
#define BINARYTREE_H

#include<functional>

using namespace std;

template <class T>
class binaryTree
{
public:
    virtual ~binaryTree() {}
    virtual bool empty() const = 0;
    virtual int size() const = 0;
    virtual void preOrder(void(*) (T *)) = 0;//先序遍歷
    virtual void  inOrder(void(*)(T *)) = 0;//中序遍歷
    virtual void postOrder(void(*)(T *)) = 0;//後序遍歷
    virtual void levelOrder(void(*)(T *)) = 0;//層次遍歷
};

#endif // BINARYTREE_H
/*
 * 隊列的定義
 * queue.h
*/
#ifndef QUEUE_H
#define QUEUE_H

#include<iostream>
using namespace std;

template <class T>
class queue
{
public:
    virtual ~queue(){}
    virtual bool empty() const = 0;
    virtual int size() const = 0;
    virtual T& front() = 0;//隊首
    virtual T& back() = 0;//隊尾
    virtual void pop() = 0;//出隊
    virtual void push(const T& theElement) = 0;//入隊
};


#endif // QUEUE_H
/*
 * 用數組表示隊列
 * arrayQueue.h
*/
#ifndef ARRAYQUEUE_H
#define ARRAYQUEUE_H

#include"queue.h"
#include"myexceptions.h"
#include<sstream>

using namespace std;

template <class T>
class arrayQueue:public queue<T>
{
public:
    arrayQueue(int initialCapacity = 10);
    ~arrayQueue(){delete [] queue;}

    bool empty() const{return theFront == theBack;}//當隊首和隊尾相等時,表示隊空
    int size() const
    {
        return (theBack - theFront + arrayLength)%arrayLength;//
    }

    T& front()//隊首元素
    {
        if(theFront == theBack)
            throw queueEmpty();//判斷隊空
        return queue[(theFront+1)%arrayLength];
    }

    T& back()//隊尾元素
    {
        if(theFront == theBack)
            throw queueEmpty();
        return queue[theBack];
    }

    void pop()//出隊
    {
        if(theFront == theBack)
            throw queueEmpty();
        theFront = (theFront+1)%arrayLength;
        queue[theFront].~T();
    }

    //入隊的實現比較複雜一些,考慮的情況比較多
    void push(const T& theELement)//入隊
    {
        //如果隊滿的時候,隊列的容量擴充一倍
        if((theBack + 1) % arrayLength == theFront)
        {
            T* newQueue = new T[2*arrayLength];

            int start = (theFront + 1)%arrayLength;
            if(start < 2)
                copy(queue+start,queue+arrayLength,newQueue);
            else
            {
                copy(queue + start,queue + arrayLength,newQueue);
                copy(queue,queue+theBack+1,newQueue+arrayLength - start);
            }

            theFront = 2 * arrayLength - 1;
            theBack = arrayLength - 2;
            arrayLength *=2;
            queue = newQueue;
        }
        theBack = (theBack + 1)%arrayLength;
        queue[theBack] = theELement;
    }


private:
    int theFront;//隊首
    int theBack;//隊尾
    int arrayLength;//數組長度
    T* queue;//存儲元素的數組
};

template <class T>
arrayQueue<T>::arrayQueue(int inititalCapacity)
{
    if(inititalCapacity < 1)
    {
        ostringstream s;
        s <<"Initial capacty = "<<inititalCapacity<<"Must be > 0";
        throw illegalParameterValue(s.str());
    }
    arrayLength = inititalCapacity;
    queue = new T[arrayLength];
    theFront = 0;
    theBack = 0;
}

#endif // ARRAYQUEUE_H
/*
 * 異常類的定義
 * myExceptions.h
*/
#ifndef MYEXCEPTIONS_H
#define MYEXCEPTIONS_H

#include<string>
#include<iostream>
using namespace std;

class illegalParameterValue
{
public:
    illegalParameterValue(string theMessage =
            "Illegal parameter value")
    {
        message = theMessage;
    }

    void outputMessage()
    {
        cout <<message<<endl;
    }
private:
    string message;
};


class queueEmpty
{
public:
    queueEmpty(string theMesssage =
            "Invalid operation on empty queue")
    {
        message = theMesssage;
    }
    void outpuMessaget()
    {
        cout<< message<<endl;
    }
private:
    string message;
};


#endif // MYEXCEPTIONS_H

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章