二叉樹以及常見面試題


 

一、什么是二叉樹

  二叉樹是每個結點最多有兩個子樹的樹結構,二叉樹是遞歸定義的,其結點有左右子樹之分,通常包含:滿二叉樹、完全二叉樹、霍夫曼樹、平衡二叉樹、紅黑樹等。

  滿二叉樹:如果二叉樹中所有分支結點的度數都為2,並且葉子結點都在統一層次上,則二叉樹為滿二叉樹,從圖形形態上看,滿二叉樹外觀上是一個三角形;從數學上看,滿二叉樹的各個層的結點數形成一個首項為1,公比為2的等比數列。如圖:

 

   完全二叉樹:完全二叉樹從根結點到倒數第二層滿足滿二叉樹,最后一層可以不完全填充,其葉子結點都靠左對齊。滿二叉樹一定是完全二叉樹,完全二叉樹不一定是滿二叉樹。

 

  非完全二叉樹:

 

 二、二叉樹常見的問題

本文包括:

2.1 二叉樹的創建

2.2 二叉樹的遍歷

2.3 求葉子節點的個數

2.4 求樹的深度

2.5 交換樹的左右子樹

2.6 判斷一個節點是否在一棵子樹中

2.7 求樹中總共的節點個數

2.8 判斷兩個樹是否相同

2.9 判斷一個樹是否為另一棵樹的子樹

  

 2.1 二叉樹的創建

  參考代碼:https://www.cnblogs.com/BeyondAnyTime/archive/2012/08/27/2659163.html

#include <QCoreApplication>
#include <QDebug>
#include <QTextStream>
//二叉樹的節點
class BinTreeNode
{
private:
    int data;
    BinTreeNode *left,*right;
public:
    //利用初始化列表完成data、left、right的初始化
    BinTreeNode(const int &item,BinTreeNode *lPtr = NULL,BinTreeNode *rPtr = NULL):data(item) ,left(lPtr),right(rPtr){}
    void set_data(int item)
    {
        data = item;
    }
    int get_data()const
    {
        return data;
    }
    void set_left(BinTreeNode *l)
    {
        left = l;
    }
    BinTreeNode *get_left() const
    {
        return left;
    }
    void set_right(BinTreeNode *r)
    {
        right = r;
    }
    BinTreeNode *get_right() const
    {
        return right;
    }
};
//二叉樹
class BinTree
{
private:
    BinTreeNode *root;
public:
    BinTree(BinTreeNode *t = NULL):root(t){}
    ~BinTree(){delete root;}
    void set_root(BinTreeNode *t)
    {
        root = t;
    }
    BinTreeNode *get_root() const
    {
        return root;
    }

    //創建二叉樹
    BinTreeNode * create_tree();

};
BinTreeNode *BinTree::create_tree()
{
    QString item;
    BinTreeNode *t,*t_l,*t_r;
    QTextStream qin(stdin);
    qin>>item;
    int data = item.toInt();

    if(item != "#")
    {
        BinTreeNode *pTmpNode = new BinTreeNode(data);
        t = pTmpNode;
        t_l = create_tree();
        t->set_left(t_l);
        t_r = create_tree();
        t->set_right(t_r);
        return t;
    }
    else
    {
        t = NULL;
        return t;
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    BinTree tree;
    qDebug()<<"輸入二叉樹前序序列進行建樹,\"#\"代表空節點"<<endl;
    tree.set_root(tree.create_tree());

    return a.exec();
}

  

   由此,可以看到,其實構造一個二叉樹並不是一個十分困難的過程,當然我們采用的是先序創建。結合自己的想法很容易寫出二叉樹:

  

#include <QCoreApplication>
#include <QDebug>
#include <QTextStream>

class binTreeTest
{
public:
    int data;
    binTreeTest *left,*right;
    void setData(int item)
    {
        data = item;
    }
    int getData()
    {
        return data;
    }
    void set_left(binTreeTest *l)
    {
        left = l;
    }
    binTreeTest * get_left()const
    {
        return left;
    }
    void set_right(binTreeTest *r)
    {
        right = r;
    }
    binTreeTest * get_right()const
    {
        return right;
    }

};
void createBinTree(binTreeTest **r)
{
    QString item;
    QTextStream qin(stdin);
    qin>>item;
    int data = item.toInt();
    binTreeTest *T_l,*T_r;
    if(item != "#")
    {
        (*r)->setData(data);
        T_l = new binTreeTest;
        T_r = new binTreeTest;
        createBinTree(&T_l);
        (*r)->set_left(T_l);

        createBinTree(&T_r);
        (*r)->set_right(T_r);
    }
    else
    {
        *r = NULL;
    }
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    binTreeTest *tree = new binTreeTest;
    qDebug()<<"輸入二叉樹前序序列進行建樹,\"#\"代表空節點"<<endl;
    createBinTree(&tree);
    return a.exec();
}

  

  在使用的過程中,注意C++的使用:https://www.cnblogs.com/pinking/p/9339201.html

 2.2 二叉樹的遍歷

   二叉樹的遍歷分為兩種策略:深度優先和廣度優先,深度優先又包含:中序遍歷、前序遍歷、后序遍歷。記憶的時候就看中間那個是什么就是什么序的遍歷。

  中序遍歷:左子樹---> 根結點 ---> 右子樹

  前序遍歷:根結點 ---> 左子樹 ---> 右子樹

  后序遍歷:左子樹 ---> 右子樹 ---> 根結點

  層次遍歷:僅僅需按層次遍歷就可以

  構造二叉樹如圖:

  輸入構造二叉樹:

  中序遍歷代碼:

void middle(binTreeTest *t)
{

    if(t != NULL)
    {
        binTreeTest *l,*r;
        l = t->get_left();
        middle(l);
        qDebug()<<t->getData();
        r = t->get_right();
        middle(r);
    }
}

  

  中序遍歷輸出結果:

  

   同理,前序遍歷應該的結果為:1、2、3、4、5、6、7、8、9  。代碼如下:

1 void forward(binTreeTest *t)
2 {
3     if(t != NULL)
4     {
5         qDebug()<<t->getData();
6         forward(t->get_left());
7         forward(t->get_right());
8     }
9 }
View Code

  后序遍歷的結果應該為:4、5、3、2、8、9、7、6、1 。代碼如下:

1 void backward(binTreeTest *t)
2 {
3     if(t != NULL)
4     {
5         backward(t->get_left());
6         backward(t->get_right());
7         qDebug()<<t->getData();
8     }
9 }
View Code

   層次遍歷,通常采用隊列的形式,保持先進先出的方式,層次遍歷每一層,結果為:1、2、6、3、7、4、5、8、9  。代碼如下:

 1 void level(binTreeTest *t)
 2 {
 3     if(t == NULL)
 4         return;
 5     QQueue<binTreeTest*> queue;
 6     queue.enqueue(t);
 7     while (!queue.empty())
 8     {
 9         binTreeTest *ptem = queue.front();
10         qDebug()<<ptem->getData();
11         queue.pop_front();
12         if(ptem->get_left() != NULL)
13         {
14             queue.push_back(ptem->get_left());
15         }
16         if(ptem->get_right() != NULL)
17         {
18             queue.push_back(ptem->get_right());
19         }
20     }
21 }
View Code

2.3 求葉子節點的個數

  求葉子節點的個數還是比較好理解的,代碼如下:

int get_leaf_num(binTreeTest *t)
{
    if(t == NULL)
        return 0;
    if(t->get_left() == NULL && t->get_right() == NULL)
    {
        return 1;
    }
    return get_leaf_num(t->get_left())+get_leaf_num(t->get_right());
}

  

2.4 求樹的深度

int getTreeHeight(binTreeTest *t)
{
    if (t == NULL)
        return 0;
    if(t->get_left() == NULL && t->get_left() == NULL)
    {
        return 1;
    }
    int l_height = getTreeHeight(t->get_left());
    int r_height = getTreeHeight(t->get_right());
    return l_height>=r_height?l_height+1:r_height+1;
}

  

2.5 交換樹的左右子樹

void swap_tree(binTreeTest *t)
{
    if(t == NULL)
        return;
    binTreeTest *temp = new binTreeTest;
    temp = t->get_right();
    t->set_right(t->get_left());
    t->set_left(temp);

    swap_tree(t->get_left());
    swap_tree(t->get_right());
}

  

2.6 判斷一個節點是否在一棵子樹中

   其實思想和遍歷是一樣的,代碼如下:

bool isInTree(binTreeTest *tree,binTreeTest *nodeTree)
{
    //查找的節點為NULL時,代表沒有查到
    if(tree == NULL)
        return false;
    else if(tree->getData() == nodeTree->getData())
    {
        return true;
    }
    else
    {
        bool has = false;
        if(tree->get_left() != NULL)
        {
            has = isInTree(tree->get_left(),nodeTree);
        }
        if((has == false) && (tree->get_right() != NULL))
        {
            has = isInTree(tree->get_right(),nodeTree);
        }
        return has;
    }
}

  

 2.7 求樹中總共的節點個數

int getNumNode(binTreeTest *t)
{
    if(t == NULL)
        return 0;
    return getNumNode(t->get_left())+getNumNode(t->get_right())+1;
}

  

 2.8 判斷兩個樹是否相同

bool compared(binTreeTest *tree,binTreeTest *tree2)
{
    //兩個樹都是空
    if(tree == NULL && tree2 == NULL)
        return true;
    if(tree == NULL || tree2 == NULL)//其中有一個為NULL就不用比了
        return false;
    if(tree->getData() == tree2->getData())
    {
        return compared(tree->get_left(),tree2->get_left())&&compared(tree->get_right(),tree2->get_right());
    }
    else
    {
        return false;
    }
}

  

 2.9 判斷一個樹是否為另一棵樹的子樹

  這個需要結合上面的判斷兩棵樹是否相同來判斷:

bool compared(binTreeTest *tree,binTreeTest *tree2)
{
    //兩個樹都是空
    if(tree == NULL && tree2 == NULL)
        return true;
    if(tree == NULL || tree2 == NULL)//其中有一個為NULL就不用比了
        return false;
    if(tree->getData() == tree2->getData())
    {
        return compared(tree->get_left(),tree2->get_left())&&compared(tree->get_right(),tree2->get_right());
    }
    else
    {
        return false;
    }
}
bool judgeNode(binTreeTest *tree,binTreeTest *child)
{
    // 兩個都是空樹
    if(tree == NULL && child == NULL)
        return true;
    // 空樹是任意的子樹
    if(child == NULL)
        return true;
    // 空樹沒有其他非空的子樹
    if(tree  == NULL)
        return false;
    //排除空樹的情況
    if(tree->getData() == child->getData())
    {
        return compared(tree,child);
    }
    else
    {
        bool result = judgeNode(tree->get_left(),child);
        if(result == false)
            return judgeNode(tree->get_right(),child);
        return result;
    }
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM