關於二叉樹的一些題

二叉樹的創建我們已經瞭解了,現在我們在這一個基礎上來拓展一些常出現的題

1、在二叉樹中尋找一個節點——>存在,返回節點,不存在返回NULL

2、求一個節點的雙親節點——>存在,返回節點,不存在返回NULL

3、求二叉樹的高度

4、求葉子節點的個數

5、求第K層的節點個數

6、求一個二叉樹的鏡像

……

不過在這這錢我們先來補充一下二叉樹的性質:

1、若規定根節點的層次爲0,則一個非空的二叉樹的第i個節點最多有2^i(i>=0)個節點。

2、若規定只有根節點的深度爲0,則深度爲K的二叉樹的最大節點數爲2^(K+1)-1(K>=-1).

3、對任何二叉樹,如果葉子節點爲n0,度爲n2的飛葉子節點的n2,則有n0=n2+1

證明過程如下:

這裏寫圖片描述

4、具有n個節點的完全二叉樹的深度K爲log2(n+1)向上取整

懂得人都知道log後面的不是2*(n+1),而是“以2爲底(n+1)”。具體怎麼驗證這個性質,那麼不妨回顧一下完全二叉樹的定義吧。而且這個性質的中心位置就是完全二叉樹。

5、對具有N的節點的完全二叉樹,如果按照從上到下,從左到右,的順序對所有的節點進行排序,從0開始編號。則:i

①、i的雙親節點爲(i-1)/2

②、i的左孩子,爲2*i+1,右孩子爲2*i+2

③、i=0,則爲根節點,無雙親節點

④2*i+1>=N,2*i+2>=N,則沒有雙親節點

瞭解了這些之後,我們再來看一下這些編程題:

1、在二叉樹中尋找一個節點——>存在,返回節點,不存在返回NULL

如果用先序的遍歷方式,那麼用遞歸的話只需四步即可

這裏寫圖片描述

非遞歸的話,也是可以參考先序遍歷的非遞歸的寫法,只需將對根節點的處理,改成和要查找的節點進行比較即可

Node* _Find(Node* pRoot, const T& value)
    {
        //遞歸:
        //if (NULL == pRoot)//樹爲空,則返回空
        //  return NULL;

        //if (pRoot->_value == value)//先確定根結點是不是所要查找的值
        //  return pRoot;

        //_Find(pRoot->_pLeft, value);//再查找左右子樹
        //_Find(pRoot->_pRight, value);

        //非遞歸:
        if (pRoot == NULL)
            return NULL;
        stack<Node*> s;
        s.push(pRoot);

        while (!s.empty())
        {
            Node *pCur = s.top();
            if (pCur->_value == value)//注意:兩個value類型不一樣
                return pCur;

            s.pop();//時刻記住,訪問完後要出棧

            if (pCur->_pRight)//注意,入棧順序是先右後左
                s.push(pCur->_pRight);
            if (pCur->_pLeft)
                s.push(pCur->_pLeft);
        }
    }

2、求一個節點的雙親節點——>存在,返回節點,不存在返回NULL

首先我們得將參數檢驗一下:對根結點檢驗,判斷樹是不是空樹;對pCur檢驗,驗證它是不是存在;還有一根特殊的情況,根結點沒有雙親。之後其他的都是普通情況。再進行遞歸運算。

Node* _Parent(Node* pRoot, Node* pCur)
    {
        if (NULL == pRoot)//首先確定樹是不是空的
            return NULL;

        //參數驗證2:判斷pCur是不是爲NULL   或    根結點是不是pCur(根結點是沒有雙親結點的)
        if (NULL == pCur || pCur == pRoot)
            return NULL;

        if (pRoot->_pLeft)//在左子樹中查找
            return _Parent(pRoot->_pLeft, pCur);

        return _Parent(pRoot->_pRight, pCur);//無論找沒找到,都返回(這一步可以拆分開)

3、求二叉樹的高度

一個二叉樹的高度是多少?如果說根結點的層數爲1的話,那最下面一層的層數就是二叉樹的高度。那麼用遞歸的話,它是左子樹的高度和右子樹高度的最大值再加1。

    size_t _Height(Node* pRoot)
    {
        if (NULL == pRoot)//沒有結點,返回0
            return 0;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
            return 1;

        //遍歷左右子樹,返回左右子樹最大值 +1(根結點)
        return max(_Height(pRoot->_pLeft),_Height(pRoot->_pRight))+1;
    }

4、求葉子節點的個數

首先想一下葉子結點的特點是啥,沒有左右子樹,所以用遞歸的話,這就是我們的一個遞歸出口,那麼葉子結點的個數就是:遞歸左子樹的個數加上遞歸右子樹的個數。

size_t _GetLeafCount(Node* pRoot)
    {
        if (NULL == pRoot)
            return 0;

        //葉子結點
        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
            return 1;

        //遞歸:左子樹的葉子結點+右子樹的葉子結點
        return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
    }

5、求第K層的節點個數

第K層的結點個數,如果K=高度,那麼這種情況和葉子結點個數還是不一樣,葉子結點不一定都在同一層,所以,我們要求第K層的需要將左子樹的第K-1層的計算出來,再加上右子樹的第K-1層結點個數。

不過這裏涉及參數檢驗的討論,對於根結點的檢驗我們已經很熟悉了,判斷其是不是空樹就行了,不過這裏還有一個參數:K

對於K來說,有一個特殊的情況那就是K==1,那麼我們就返回1,至於對其進行參數檢驗,只要其不小於1就行了;那麼你或許會問了,爲什麼不對它進行上限處理呢?需要嗎?

/*假設我們進行了上限處理:K<Height(),我們這是遞歸,每一次進入這個函數走到這一步是不是都需要求一下高度,每次求高度還要將樹遍歷一下,那麼算下來我們求了幾次了,很多。而不加的時候會出錯嗎?我們假設K是5,(層數是3),那麼到第四層(所有節點都是NULL)直接會從判斷根結點爲空的地方返回,所以,這是不會影響結果的。*/

//第k層的結點個數
    size_t _GetKLevelCount(Node* pRoot, size_t k)
    {
        if (NULL == pRoot || k < 1)//注意:參數檢驗
            return 0;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
            return 1;

        return _GetKLevelCount(pRoot->_pLeft, k - 1) + _GetKLevelCount(pRoot->_pRight, k - 1);
    }

6、求一個二叉樹的鏡像

這裏寫圖片描述

如圖所示,我們看到,左邊的是我們一直使用的一個二叉樹,鏡像之後我們可以直觀上看到,兩個二叉樹的關係:關於中間的一條隱形的線(鏡子)對稱。那麼接下來我們就來根據這一個性質來求一個二叉樹的鏡像

首先我們處理的是二叉樹的特殊情況:

1、要是樹爲空,那麼就不用處理了,直接返回就行了

2、鏡像是將二叉樹中的左子樹和右子樹交換,那麼如果這個數只有一個節點,那麼就不用交換了。所以這一個情況是,二叉樹只有一個節點。

3.普通情況:這要是用遞歸的話,很簡單的幾句代碼就搞定了:首先是將根節點的兩個子樹交換,之後再進入左子樹進行遞歸,然後再進入右子樹進行遞歸。

這樣之後,就完了,不過非遞歸又該怎麼寫呢?

同樣我們來屢一下思路:

1、首先還式對樹進行判斷:樹空的話,返回NULL

2、其次創建一個數據結構(隊列),將根節點入隊列

3、取出對頭元素,判斷其左右子樹,存在,則入隊列

4、對頭元素的左右子樹。最後將對頭元素出隊列

    void _BinaryMirror(Node* pRoot)
    {
        if (pRoot == NULL)
            return;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
            return;

        swap(pRoot->_pLeft, pRoot->_pRight);

        if (pRoot->_pLeft!=NULL)
            _BinaryMirror(pRoot->_pLeft);
        if (pRoot->_pRight!=NULL)
            _BinaryMirror(pRoot->_pRight);
    }

    void _BinaryMirror_Nor(Node* pRoot)
    {
        if (pRoot == NULL)
            return;

        queue<Node*> q;
        q.push(pRoot);

        while (!q.empty())
        {
            Node* pCur = q.front();//取隊頭元素

            //如果左右孩子存在,將其保存在隊列中
            if (pCur->_pLeft)
                q.push(pCur->_pLeft);
            if (pCur->_pRight)
                q.push(pCur->_pRight);

            //交換左右
            swap(pCur->_pLeft, pCur->_pRight);

            //隊頭元素出隊列
            q.pop();
        }
    }

這裏寫圖片描述
下面是完整的代碼,以及測試的語句:

#include<iostream>
using namespace std;

#include<queue>
#include<stack>

// 孩子表示法
template<class T>
struct BinaryTreeNode
{
    BinaryTreeNode(const T& value)
    : _value(value)
    , _pLeft(NULL)
    , _pRight(NULL)
    {}

    T _value;
    BinaryTreeNode<T>* _pLeft;   // 左孩子
    BinaryTreeNode<T>* _pRight;  // 右孩子
};


template<class T>
class BinaryTree
{
    typedef BinaryTreeNode<T> Node;//換個名字Node
public:
    //構造函數①
    BinaryTree()
        :_pRoot(NULL)
    {}

    //構造函數②
    BinaryTree(const T array[], size_t size, const T& invalid)
    {
        size_t index = 0;
        _CreateBinaryTree(_pRoot, array, size, index, invalid);
    }

    //拷貝構造函數
    BinaryTree(const BinaryTree<T>& bt)
    {
        _pRoot = _CopyBirnaryTree(bt._pRoot);
    }

    //賦值運算符重載
    BinaryTree<T>& operator=(const BinaryTree<T>& bt)
    {
        if (this != &bt)
        {
            _CopyBirnaryTree(bt._pRoot);
        }
        return this;
    }

    //析構函數
    ~BinaryTree()
    {
        _DestroyBinaryTree(_pRoot);
    }

    // 層序遍歷
    void LevelOrder()
    {
        cout << "LevelOrder:" << endl;
        _LevelOrder(_pRoot);
        cout << endl;
    }

    //查找函數
    Node* Find(const T& value)
    {
        return _Find(_pRoot, value);
    }

    // 找一個結點的雙親結點
    Node* Parent(Node* pCur)
    {
        return _Parent(_pRoot, pCur);
    }

    //找一個結點的左孩子結點
    Node* GetLeftChild(Node* pCur)
    {
        return (pCur == NULL) ? NULL : pCur->_pLeft;
    }

    //找一個結點的右孩子結點
    Node* GetRightChild(Node* pCur)
    {
        return (pCur == NULL) ? NULL : pCur->_pRight;
    }

    //求二叉樹的高度
    size_t Height()
    {
        return _Height(_pRoot);
    }

    //求二叉樹葉子結點的個數
    size_t GetLeafCount()
    {
        return _GetLeafCount(_pRoot);
    }

    //求第K層結點的個數
    size_t GetKLevelCount(size_t k)
    {
        return _GetKLevelCount(_pRoot, k);
    }

    //求二叉樹的鏡像    (非遞歸)
    void BinaryMirror_Nor()
    {
        _BinaryMirror_Nor(_pRoot);
    }

    //求二叉樹的鏡像
    void BinaryMirror()
    {
        _BinaryMirror(_pRoot);
    }

private:
    //創建二叉樹
    void _CreateBinaryTree(Node* &pRoot, const T array[], size_t size, size_t& index, const T& invalid)
    {
        if (index < size&&array[index] != invalid)
        {
            pRoot = new Node(invalid);//注意new的時候要和結構體中寫的函數參數對應
            pRoot->_value = array[index];

            _CreateBinaryTree(pRoot->_pLeft, array, size, ++index, invalid);//注意:++index
            _CreateBinaryTree(pRoot->_pRight, array, size, ++index, invalid);
        }
    }

    // pRoot-->被拷貝樹的根節點
    Node* _CopyBirnaryTree(Node* pRoot)
    {
        if (pRoot == NULL)
            return NULL;
        Node *NewRoot = new Node(pRoot->_value);
        NewRoot->_pLeft = _CopyBirnaryTree(pRoot->_pLeft);
        NewRoot->_pRight = _CopyBirnaryTree(pRoot->_pRight);
        return NewRoot;
    }

    //銷燬二叉樹
    void _DestroyBinaryTree(Node*& pRoot)
    {
        Node* temp = pRoot;
        if (temp == NULL)
            return;
        _DestroyBinaryTree(temp->_pLeft);
        _DestroyBinaryTree(temp->_pRight);
        delete temp;
        temp = NULL;
    }


    Node* _Find(Node* pRoot, const T& value)
    {
        //遞歸:
        //if (NULL == pRoot)//樹爲空,則返回空
        //  return NULL;

        //if (pRoot->_value == value)//先確定根結點是不是所要查找的值
        //  return pRoot;

        //_Find(pRoot->_pLeft, value);//再查找左右子樹
        //_Find(pRoot->_pRight, value);

        //非遞歸:
        if (pRoot == NULL)
            return NULL;
        stack<Node*> s;
        s.push(pRoot);

        while (!s.empty())
        {
            Node *pCur = s.top();
            if (pCur->_value == value)//注意:兩個value類型不一樣
                return pCur;

            s.pop();//時刻記住,訪問完後要出棧

            if (pCur->_pRight)//注意,入棧順序是先右後左
                s.push(pCur->_pRight);
            if (pCur->_pLeft)
                s.push(pCur->_pLeft);
        }
    }

    //查找pCur的雙親結點
    Node* _Parent(Node* pRoot, Node* pCur)
    {
        if (NULL == pRoot)//首先確定樹是不是空的
            return NULL;

        //參數驗證2:判斷pCur是不是爲NULL   或    根結點是不是pCur(根結點是沒有雙親結點的)
        if (NULL == pCur || pCur == pRoot)
            return NULL;

        if (pRoot->_pLeft)//在左子樹中查找
            return _Parent(pRoot->_pLeft, pCur);

        return _Parent(pRoot->_pRight, pCur);//無論找沒找到,都返回(這一步可以拆分開)
    }

    size_t _Height(Node* pRoot)
    {
        if (NULL == pRoot)//沒有結點,返回0
            return 0;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
            return 1;

        //遍歷左右子樹,返回左右子樹最大值 +1(根結點)
        return max(_Height(pRoot->_pLeft),_Height(pRoot->_pRight))+1;
    }

    size_t _GetLeafCount(Node* pRoot)
    {
        if (NULL == pRoot)
            return 0;

        //葉子結點
        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
            return 1;

        //遞歸:左子樹的葉子結點+右子樹的葉子結點
        return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
    }

    //第k層的結點個數
    size_t _GetKLevelCount(Node* pRoot, size_t k)
    {
        if (NULL == pRoot || k < 1)//注意:參數檢驗
            return 0;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
            return 1;

        return _GetKLevelCount(pRoot->_pLeft, k - 1) + _GetKLevelCount(pRoot->_pRight, k - 1);
    }

    void _BinaryMirror(Node* pRoot)
    {
        if (pRoot == NULL)
            return;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
            return;

        swap(pRoot->_pLeft, pRoot->_pRight);

        if (pRoot->_pLeft!=NULL)
            _BinaryMirror(pRoot->_pLeft);
        if (pRoot->_pRight!=NULL)
            _BinaryMirror(pRoot->_pRight);
    }

    void _BinaryMirror_Nor(Node* pRoot)
    {
        if (pRoot == NULL)
            return;

        queue<Node*> q;
        q.push(pRoot);

        while (!q.empty())
        {
            Node* pCur = q.front();//取隊頭元素

            //如果左右孩子存在,將其保存在隊列中
            if (pCur->_pLeft)
                q.push(pCur->_pLeft);
            if (pCur->_pRight)
                q.push(pCur->_pRight);

            //交換左右
            swap(pCur->_pLeft, pCur->_pRight);

            //隊頭元素出隊列
            q.pop();
        }
    }

private:
    Node* _pRoot;   // 指向樹的根節點
};

void FunTest2()
{
    char* pStr = "124###35##6";
    BinaryTree<char> bt(pStr, strlen(pStr), '#');

    //cout << bt.Find('5')->_value << endl;//注意:傳參的類型
    ////cout << bt.Parent(bt.Find('5'))->_value << endl;
    //cout << bt.Height() << endl;//高度

    //cout << bt.GetLeafCount() << endl;//葉子結點個數

    //cout << bt.GetKLevelCount(3) << endl;

    //bt.BinaryMirror();            //????
    //bt.BinaryMirror_Nor();
}


int main()
{
    FunTest2();
    return 0;
}
發佈了121 篇原創文章 · 獲贊 90 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章