n叉树遍历 《算法导论》10.4-4 10.4-6

  • 与二叉树不同的是,n叉树上每个节点可以有任意个孩子节点。由于孩子节点数目不确定,我们不知道该为每个节点分配多大的存储空间,就算可以确定孩子节点的最大数目,也不能保证每个节点拥有相同个数的孩子,这就会导致为孩子节点预留的空间得不到充分利用。

    有一种表示法可以避免这些问题,那就是“左孩子-右兄弟”表示法,与二叉树类似,每个节点依然保存除了父亲节点外的两个节点指针,分别表示其左数第一个孩子,和右面第一个兄弟。

    这样一来,就可以像遍历二叉树一样地遍历n叉树了。10.4-4具体实现见Traverse方法。

    10.4-6要求在Node节点里用“两个指针和一个bool量”取代“三个指针”,实现获取当前节点的父亲和孩子节点的过程,假设当前节点的孩子个数为m,要求过程时间为O(m)。

    这道题的思路是,要想从三个指针变成两个指针,应该省略谁呢?仔细观察会发现,一个节点的若干个孩子节点的父亲指针都指向自己,这里有信息的冗余,应该想办法把保存多份父亲指针变成只保存一份,这一份保存在哪里好呢?因为最右面的孩子没有右兄弟,它的“右兄弟”指针的位置刚好空出来可以保存“父亲”指针,而题目中提示的那个bool量自然就是用来表示“当前节点是否为最右面的孩子”。具体实现见GetParent,GetChild方法。

#include <cassert>
#include <iostream>
#include <stack>
using namespace std;
using Index = int;
class ArbitraryRootTree
{
public:
    ArbitraryRootTree();
    //O(n)-time traverse
    void Traverse() const;

    Index GetParent(Index nodeIndex) const;
    Index GetChild(Index nodeIndex, int rank) const;
private:
    struct Node
    {
        int key;
        Index left_child;
        Index right_sibling;
        bool  bLastChild;
    };
    Node m_array[11];
    Index m_root;
};

ArbitraryRootTree::ArbitraryRootTree()
{
/* the tree looks like
 * node's key                node's index in array
 *          12                          6
 *     /    |    \
 *    1     3      5            5       4       3
 *   / \    |    / | \ \
 *  7  8    4   2  6 0  11    2  1      0     7 8 9 10
 *
 * index  key left_child right_sibling parent
 *  0       4       -1      -1          4
 *  1       8       -1      -1          5
 *  2       7       -1      1
 *  3       5       7       -1          6
 *  4       3       0       3
 *  5       1       2       4
 *  6      12       5       -1
 *  7       2       -1      8
 *  8       6       -1      9
 *  9       0       -1      10
 *  10      11      -1      -1          3
 *
*/
    m_root = 6;
    m_array[0] = Node{4, -1, 4, true};
    m_array[1] = Node{8, -1, 5, true};
    m_array[2] = Node{7, -1, 1, false};
    m_array[3] = Node{5, 7, 6, true};
    m_array[4] = Node{3, 0, 3, false};
    m_array[5] = Node{1, 2, 4, false};
    m_array[6] = Node{12, 5, -1, false};
    m_array[7] = Node{2, -1, 8, false};
    m_array[8] = Node{6, -1, 9, false};
    m_array[9] = Node{0, -1, 10, false};
    m_array[10] = Node{11, -1, 3, true};
}

void ArbitraryRootTree::Traverse() const
{
    stack<Index> indexStack;
    indexStack.push(m_root);
    Index index, childIndex;
    while(!indexStack.empty())
    {
        index = indexStack.top();
        indexStack.pop();
        cout << m_array[index].key << "\t";
        childIndex = m_array[index].left_child;
        if(childIndex != -1)
            indexStack.push(childIndex);
        childIndex = m_array[index].right_sibling;
        if(!m_array[index].bLastChild && childIndex != -1)
            indexStack.push(childIndex);
    }
}

Index ArbitraryRootTree::GetParent(Index nodeIndex) const
{
    while(m_array[nodeIndex].bLastChild == false)
        nodeIndex = m_array[nodeIndex].right_sibling;
    return m_array[nodeIndex].right_sibling;
}

Index ArbitraryRootTree::GetChild(Index nodeIndex, int rank) const
{
    Index childIndex = m_array[nodeIndex].left_child;
    while(rank-- != 1)
    {
        assert(childIndex != -1);
        childIndex = m_array[childIndex].right_sibling;
    }
    return childIndex;
}
void Test()
{
    ArbitraryRootTree tree;
    tree.Traverse(); //12   1   3   5   2   6   0   11  4   7   8
    cout << endl;
    cout << tree.GetParent(7) << "\t";
    cout << tree.GetParent(8) << "\t";
    cout << tree.GetParent(9) << "\t";
    cout << tree.GetParent(10) << "\t";//下标为7~10四个节点的父亲节点下标都是3
    cout << endl;
    cout << tree.GetChild(3,1) << "\t";
    cout << tree.GetChild(3,2) << "\t";
    cout << tree.GetChild(3,3) << "\t";
    cout << tree.GetChild(3,4) << "\t";//依次获取节点3的四个孩子7~10
}


//Test运行结果
//12    1   3   5   2   6   0   11  4   7   8
//3    3    3    3
//7    8    9    10

转载于:https://www.cnblogs.com/meixiaogua/p/9798064.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值