• 分层遍历二叉树


    分层遍历二叉树
    《编程之美》3.10
    问题:给定一颗二叉树,要求按分层遍历该二叉树,即从上到下按层次访问该二叉树(每一层单独输出一行),每一层要求访问的顺序为从左到右,并将节点依次编号。
    解:
    算法1
    printNodeAtLevel(Node *root, int level)
    if(level == 0 && root)
       输出root->data
       return
    if(root==NULL)
       return
    printNodeAtLevel(root->left, level-1)
    printNodeAtLevel(root->right, level-1)
    如果写成:
    printNodeAtLevel(Node *root, int level)
    if(level == 0)
       输出root->data
       return
    if(root->left)
       printNodeAtLevel(root->left, level-1)
    if(root->right)
       printNodeAtLevel(root->right, level-1)
    可能出现的问题是:当二叉树为空时,在调用printNodeAtLevel的函数中需要判断:
    if(root)
    printNodeAtLevel(root,level)
    而如果写成第一种形式,则无需判断
    设二叉树的深度是deep
    printTreeByLevel()
    for(i=0;i<deep;i++)
       printfNodeAtLevel(root,i)
    算法2
    分析该算法,需额外求出二叉树的深度deep。
    遍历每层节点均从根root开始,造成效率低下。在访问第k层节点的时候,只需要第k-1层的节点信息。因此,可以在打印第k-1层节点时,保留第k-1层的节点信息,从而避免每层遍历都从root开始。
    vector<Node*> vecs; //用于保存各层节点信息
    printNodeAtLevel(Node *root, int level)
    if(level == 0 && root)
       输出root->data
       return
    if(root==NULL)
       return
    vec.push_back(root)
    printNodeAtLevel(root->left, level-1)
    printNodeAtLevel(root->right, level-1)
    printTreeByLevel(Node *root)
    vec.push_back(root)
    cur=0
    while(cur != vec.size())
       last = vec.size()
       while(cur != last)
        cout << vec[cur]->data<< “ ”;
        if(root->left)
         vec.push_back(root->left)
        if(root->right)
         vec.push_back(root->right)
        cur ++
       输出换行符
    算法3:BFS
    使用BFS的关键问题在于:输出换行符。采用了两种方法:一种是在每一行后插入一个空指针NULL来表示一行的结束;另一种方法使用两个队列,一个que1用来表示当前层的节点,另一个que2用来表示下一层的节点。
    方法1
    que.push(root)
    while(true)
    last=vec.size()
    while(cur < lase)
       p=que[cur++]
       if(!p)
        cout << endl
        vec.push(0)
        break
       cout<<p->data
       if(p->left)
        vec.push(p->left)
       if(p->right)
        vec.push(p->right)
    if(cur == que.size()-1 && que[cur]==NULL)
       break
    方法2:使用两个队列
    que1.push(root)
    while(true)
    while(que1非空)
       p= que1.pop()
       输出p
       if(p->left)
        que2.push(p->left)
       if(p->right)
        que2.push(p->right)
    输出换行符
    que1.swap(que2)
    if(que1为空)
    break
    扩展问题:
    要求按深度从下到上访问二叉树,每层的访问顺序依然是从左到右。
    解:首先把各层节点(以0作为层的分隔符)存放在一个vec中,然后反向遍历vec
    此时,需要注意的地方是:节点入vec的顺序是先右节点再左节点。

    具体代码如下:
    #include <iostream>
    #include <vector>
    #include <queue>
    #include <deque>

    using namespace std;

    class Node{
    public:
    Node(int d):data(d), left(NULL), right(NULL){}

    int data;
    Node *left;
    Node *right;
    };

    //algorithm 1
    void printNodeAtLevelRecursion(Node *root, int level){
    if (level == 0 && root)
    {
       cout << root->data << " ";
       return;
    }

    if (root == NULL)
       return;

    if (root->left)
       printNodeAtLevelRecursion(root->left, level-1);
    if (root->right)
       printNodeAtLevelRecursion(root->right, level - 1);
    }

    int comDeep(Node *root){
    if (root == NULL)
       return 0;
    int leftDeep = comDeep(root->left);
    int rightDeep = comDeep(root->right);
    return leftDeep > rightDeep ? leftDeep + 1 : rightDeep + 1;
    }

    void printNode(Node *root){
    int deep = comDeep(root);

    for (int level = 0; level < deep; level ++){
       printNodeAtLevelRecursion(root, level);
       cout << endl;
    }
    }

    // algorithm2
    void printNodeAtLevel2(Node *root){
    vector<Node*> vec;
    vec.push_back(root);
    int cur = 0;
    int last;

    while (true)
    {
       last = vec.size();
       while (cur < last)
       {
        Node *p = vec[cur];
        cout << p->data << " ";
        cur ++;
        if (p->left)
         vec.push_back(p->left);
        if (p->right)
         vec.push_back(p->right);
       }
       cout << endl;

       if (cur == vec.size())
        break;
    }
    }

    //algorithm3:BFS
    void printNodeAtLevelBFS1(Node *root){
    queue<Node*> que;
    que.push(root);
    que.push(0);

    while (true)
    {
       int size = que.size();
       while (true)
       {
        Node *p = que.front();
        que.pop();
        if (p == NULL){
         cout << endl;
         que.push(0);
         break;
        }

        cout << p->data << " ";
        if (p->left)
         que.push(p->left);
        if (p->right)
         que.push(p->right);
       }

       if (que.size() == 1 && que.front() == NULL)
        break;
    }
    }

    void printNodeAtLevelBFS2(Node *root){
    deque<Node*> que1, que2;
    que1.push_back(root);

    while (true)
    {
       while (!que1.empty())
       {
        Node *p = que1.front();
        que1.pop_front();

        cout << p->data << " ";
        if (p->left)
         que2.push_back(p->left);
        if (p->right)
         que2.push_back(p->right);
       }

       cout << endl;
       que1.swap(que2);
       if (que1.empty())
        break;
    }
    }

    //extensive question1:print node at level form down to up;point at each level: from left to right
    void printNodeAtLevelDown2UpLeft2Right(Node *root){
    vector<Node *> vec;
    vec.push_back(root);
    vec.push_back(0);
    int cur = 0;

    while (true)
    {
       while (true)
       {
        Node *p = vec[cur ++];
        if (p == NULL)
        {
         vec.push_back(0);
         break;
        }

        if (p->right)
         vec.push_back(p->right);
        if (p->left)
         vec.push_back(p->left);
       }

       if (cur == vec.size()-1 && vec[cur] == 0)
        break;
    }

    vec.pop_back();
    //traverse the vec in reverse order
    cur = vec.size() - 2;
    while (cur >= 0)
    {
       if (vec[cur] == NULL)
       {
        cout << endl;
       }
       else{
        cout << vec[cur]->data << " ";
       }
       cur --;
    }
    cout << endl;
    }

    //extensive question1:print node at level form down to up;point at each level: from right to left
    void printNodeAtLevelDown2UpRight2Left(Node *root){
    vector<Node *> vec;
    vec.push_back(root);
    vec.push_back(0);
    int cur = 0;

    while (true)
    {
       while (true)
       {
        Node *p = vec[cur ++];
        if (p == NULL)
        {
         vec.push_back(0);
         break;
        }
       
        if (p->left)
         vec.push_back(p->left);
        if (p->right)
         vec.push_back(p->right);
       }
      
       if (cur == vec.size()-1 && vec[cur] == 0)
        break;
    }

    vec.pop_back();
    //traverse the vec in reverse order
    cur = vec.size() - 2;
    while (cur >= 0)
    {
       if (vec[cur] == NULL)
       {
        cout << endl;
       }
       else{
        cout << vec[cur]->data << " ";
       }
       cur --;
    }
    cout << endl;
    }


    int main(){
    Node *p1 = new Node(1);
    Node *p2 = new Node(2);
    Node *p3 = new Node(3);
    Node *p4 = new Node(4);
    Node *p5 = new Node(5);
    Node *p6 = new Node(6);
    Node *p7 = new Node(7);
    Node *p8 = new Node(8);
    p1->left = p2;
    p1->right = p3;
    p2->left = p4;
    p2->right = p5;
    p3->right = p6;
    p5->left = p7;
    p5->right = p8;
    Node *root = p1;

    cout << "test printNode***********" << endl;
    printNode(root);
    cout << "test printNodeAtLevel2************" << endl;
    printNodeAtLevel2(root);
    cout << "test printNodeAtLevelBFS1**************" << endl;
    printNodeAtLevelBFS1(root);
    cout << "test printNodeAtLevelBFS2**************" << endl;
    printNodeAtLevelBFS2(root);
    cout << "test printNodeAtLevelDown2UpLeft2Right**************" << endl;
    printNodeAtLevelDown2UpLeft2Right(root);
    cout << "test printNodeAtLevelDown2UpRight2Left**************" << endl;
    printNodeAtLevelDown2UpRight2Left(root);
    return 0;
    }

    之前重温本书写书评时,也尝试找寻更好的编程解法。今天把另一个问题的实现和大家分享。

    问题定义

    给定一棵二叉树,要求按分层遍历该二叉树,即从上到下按层次访问该二叉树(每一层将单独输出一行),每一层要求访问的顺序为从左到右,并将节点依次编号。下面是一个例子:

    输出:

    1

    2 3

    4 5 6

    7 8

    节点的定义:

    ?

    1

    2

    3

    4

    5

    struct Node {

        Node *pLeft;

        Node *pRight;

        int data;

    };

    书上的解法

    书上举出两个解法。第一个解法是用递归方式,搜寻并打印某一层的节点,再打印下一层的节点。这方法简单但时间效率不高(但不需要额外空间),因此书中亦提供了第二个解法。

    书中第二个解法,使用vector容器来储存n个节点信息,并用一个游标变量last记录前一层的访问结束条件,实现如下:

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    void PrintNodeByLevel(Node* root) {

         vector<Node*> vec; // 这里我们使用STL 中的vector来代替数组,可利用到其动态扩展的属性

         vec.push_back(root);

         int cur = 0;

         int last = 1;

         while(cur < vec.size()) {

              Last = vec.size(); // 新的一行访问开始,重新定位last于当前行最后一个节点的下一个位置

              while(cur < last) {

                   cout << vec[cur] -> data << " "; // 访问节点

                   if(vec[cur] -> lChild) // 当前访问节点的左节点不为空则压入

                       vec.push_back(vec[cur] -> lChild);

                   if(vec[cur] -> rChild) // 当前访问节点的右节点不为空则压入,注意左右节点的访问顺序不能颠倒

                       vec.push_back(vec[cur] -> rChild);

                   cur++;

              }

              cout << endl; // 当cur == last时,说明该层访问结束,输出换行符

         }

    }

    广度优先搜索

    书中没有提及,本问题其实是以广度优先搜索(breath-first search, BFS)去遍历一个树结构。广度优先搜索的典型实现是使用队列(queue)。其伪代码如下:

    ?

    1

    2

    3

    4

    5

    6

    7

    enqueue(Q, root)

    do

        node = dequeue(Q)

        process(node) //如把内容列印

        for each child of node

            enqueue(Q, child)

    while Q is not empty

    书上的解法,事实上也使用了一个队列。但本人认为,使用vector容器,较不直觉,而且其空间复杂度是O(n)。

    如果用队列去实现BFS,不处理换行,能简单翻译伪代码为C++代码:

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    void PrintBFS(Node* root) {

        queue<Node*> Q;

        Q.push(root);

        do {

            Node *node = Q.front();

            Q.pop();

            cout << node->data << " ";

            if (node->pLeft)

                Q.push(node->pLeft);

            if (node->pRight)

                Q.push(node->pRight);

        }

        while (!Q.empty());

    }

    本人觉得这样的算法实现可能比较清楚,而且空间复杂度只需O(m),m为树中最多节点的层的节点数量。最坏的情况是当二叉树为完整,m = n/2。

    之后的难点在于如何换行。

    本人的尝试之一

    第一个尝试,利用了两个队列,一个储存本层的节点,另一个储存下层的节点。遍历本层的节点,把其子代节点排入下层队列。本层遍历完毕后,就可换行,并交换两个队列。

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    void PrintNodeByLevel(Node* root) {

        deque<Node*> Q1, Q2;

        Q1.push_back(root);

        do {

            do {

                Node* node = Q1.front();

                Q1.pop_front(); // Removes the first element in the list container, effectively reducing the list size by one.

                cout << node->data << " ";

                if (node->pLeft)

                    Q2.push_back(node->pLeft);

                if (node->pRight)

                    Q2.push_back(node->pRight);

            } while (!Q1.empty()); //q1不为空时把下一层的值都压入到q2中

            cout << endl;

            Q1.swap(Q2);  //交换q1,q2,此时q2为空,q1是上层的值

        } while(!Q1.empty());

    }

    本实现使用deque而不是queue,因为deque才支持swap()操作。注意,swap()是O(1)的操作,实际上只是交换指针。

    这实现要用两个循环(书上的实现也是),并且用了两个队列。能够只用一个循环、一个队列么?

    本人的尝试之二

    换行问题其实在于如何表达一层的结束。书上采用了游标,而第一个尝试则用了两个队列。本人想到第三个可行方案,是把一个结束信号放进队列里。由于使用queue<Node*>,可以插入一个空指针去表示一层的遍历结束。

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    void PrintNodeByLevel(Node* root) {

        queue<Node*> Q;

        Q.push(root);

        Q.push(0);

        do {

            Node* node = Q.front();

            Q.pop();

            if (node) {

                cout << node->data << " ";

                if (node->pLeft)

                    Q.push(node->pLeft);

                if (node->pRight)

                    Q.push(node->pRight);

            }

            else if (!Q.empty()) {

                Q.push(0);

                cout << endl;

            }

        } while (!Q.empty());

    }

    这个实现的代码很贴近之前的PrintBFS(),也只有一个循环。注意一点,当发现空指针(结束信号)时,要检查队列内是否还有节点,如果没有的话还插入新的结束信号,则会做成死循环。

    测试代码

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    void Link(Node* nodes, int parent, int left, int right) {

        if (left != -1)

            nodes[parent].pLeft = &nodes[left]; 

      

        if (right != -1)

            nodes[parent].pRight = &nodes[right];

    }

      

    void main()

    {

        Node test1[9] = { 0 };

          

        for (int i = 1; i < 9; i++)

            test1[i].data = i;

      

        Link(test1, 1, 2, 3);

        Link(test1, 2, 4, 5);

        Link(test1, 3, 6, -1);

        Link(test1, 5, 7, 8);

      

        PrintBFS(&test1[1]);

        cout << endl << endl;

      

        PrintNodeByLevel(&test1[1]);

        cout << endl;

    }

    结语

    第一个尝试是几个月前做的,没想到今晚写博文又想到了第二个尝试。两个尝试难分优劣,但两种思维或许也可以解决其他问题。还有其他方法么?

  • 相关阅读:
    (转载)什么时候需要用到try-catch
    直接打印Java的对象时输出的到底是什么
    关于图像语义分割的总结和感悟(转载)
    面经
    石家庄停车位在线预约平台16
    石家庄停车位在线预约平台15
    石家庄停车位在线预约平台14
    石家庄停车位在线预约平台13
    石家庄停车位在线预约平台12
    石家庄停车位在线预约平台11
  • 原文地址:https://www.cnblogs.com/fickleness/p/3154974.html
Copyright © 2020-2023  润新知