[置顶] C++非递归遍历删除树节点——广度优先,逐层删除

C++标准库stl中没有现成的树的数据结构,但是一般都可以通过已有数据结构自定义获得:

假设定义树节点如下:(非二叉树,普通的树)

//****************************************************************//
//****节点                                                     ****//
//****************************************************************//
template <class T>
struct TreeNode {
    T _data;
    TreeNode *_parent;
    list<TreeNode*> _children;
    TreeNode(const T &data) : _data(data), _parent(NULL) {}
    const T &data() { return _data; }
    bool setParent(TreeNode *parent)//挂接父节点
    {
        if (parent == this->_parent)//如果相同,则无需变动
            return true;
        if (parent)//加入新父节点的子节点列表中
        {//注意:如果本节点为新父节点的某个祖先节点(包括新父节点本身),则不允许建立;
            TreeNode *ancestor = parent;
            while (ancestor && ancestor != this) ancestor = ancestor->_parent;
            if (ancestor) return false;//禁止自循环的树,否则在递归时会引起混乱
            list<TreeNode*>::const_iterator it_new = parent->_children.begin();
            while (it_new != parent->_children.end() && (*it_new) != this) ++it_new;
            if (it_new == parent->_children.end()) parent->_children.push_back(this);
        }
        if (this->_parent)//从旧的父节点的子节点列表中删除
        {
            list<TreeNode*>::const_iterator it_old = this->_parent->_children.begin();
            while (it_old != this->_parent->_children.end() && (*it_old) != this) ++it_old;
            if (it_old != this->_parent->_children.end()) this->_parent->_children.erase(it_old);
        }
        this->_parent = parent;//变更父节点指向
        return true;
    }
};

熟悉算法的都知道,在树的遍历中,通常最直观最易想到的是递归方式,但是递归是深度优先的,对栈的开销大,还很慢、很低效;

所以经常有一些对递归算法的优化、替代,比如将线性递归用线性迭代替换、或者优化成尾递归(由编译器对尾递归优化)等等;

但是非线性结构的递归(比如树),通常不易直观地改成迭代方式,这就要按需要实现的具体功能来看,可以如何替换。

就树的遍历或者删除而言,可以通过广度优先的方式,替换深度优先的递归;

以删除为例(删除的情况也最为复杂,因为会在遍历中销毁树节点,破坏原来的结构),代码如下:

template <class T>
void Tree<T>::deleteSubTree(TreeNode<T> *node)
{//利用层级遍历方式遍历删除节点,以广度优先方式,避免递归方式的深度优先导致的低效
    list<TreeNode<T>*> levelRoll0,levelRoll1;//准备两个容器,存放相邻两层节点;
    if (node)
    {
        node->setParent(NULL);
        levelRoll0.push_back(node);
    }
    int roll = 0;//表示轮次,标明两个容器轮换的次序
    while ((roll ? levelRoll1.size() : levelRoll0.size()) > 0)
    {
        if (roll)
        {
            levelRoll0.clear();
            list<TreeNode<T>*>::iterator it_level = levelRoll1.begin();
            while (it_level != levelRoll1.end())
            {
                list<TreeNode<T>*>::const_iterator it_child = (*it_level)->_children.begin();
                while (it_child != (*it_level)->_children.end())
                {
                    levelRoll0.push_back(*it_child);//保存这层节点,下次删除
					(*it_child)->setParent(NULL);//解除与将要被删除的父节点的链接关系
                    it_child = (*it_level)->_children.begin();//注意此时it_child已失效,必须重置
                }
                delete (*it_level);//删除该节点
                ++it_level;
            }
            roll = 0;
        }
        else
        {
            levelRoll1.clear();
            list<TreeNode<T>*>::iterator it_level = levelRoll0.begin();
            while (it_level != levelRoll0.end())
            {
                list<TreeNode<T>*>::const_iterator it_child = (*it_level)->_children.begin();
                while (it_child != (*it_level)->_children.end())
                {
                    levelRoll1.push_back(*it_child);//保存这层节点,下次删除
					(*it_child)->setParent(NULL);//解除与将要被删除的父节点的链接关系
                    it_child = (*it_level)->_children.begin();//注意此时it_child已失效,必须重置
                }
                delete (*it_level);//删除该节点
                ++it_level;
            }
            roll = 1;
        }
    }
}

基本思路为,利用两个list容器, 轮流替换树的每相邻两个层次的节点,逐层进行销毁(广度优先);

欢迎大家拍板与指正!

你可能感兴趣的:(C++,算法,删除,遍历,树)