二叉树

二叉树

1. 用数组表示完全二叉树

i:下标位置, n:长度-1

2 * i + 1 ≤ n   左结点

2 * i + 2 ≤ n   右结点

2. 二叉树的遍历

深度遍历和广度遍历

深度遍历:

前序、中序、后序

递归:

迭代:

非递归——前序遍历

根——左——右
处理完根节点后, 我们还需要保存根节点来找到右子树
使用栈来保存每一棵子树的根
 

 

void VLR_() // 非递归前序遍历
{
    if (rbt == nullptr)
        return ;

    stack<Node *> s;
    root pRoot = rbt; // 根节点

    while (1)
    {
        if (pRoot != nullptr)
        {
            cout << pRoot->value << endl;
            s.push(pRoot);
            pRoot = pRoot->pLChild;
        }
        else
        {
            if (s.size() == 0)
                break;
            pRoot = s.top()->pRChild;
            s.pop();
        }
    }


}

 

 

非递归——中序遍历

左——根——右
将当前节点入栈
将当前结点变为左结点操作
当前结点为空, 出栈, 打印, 将当前结点变为右结点操作
void LDR_() // 非递归中序遍历
{
    if (rbt == nullptr)
        return;

    stack<Node *> s;
    root pRoot = rbt; // 根节点

    while (1)
    {
        if (pRoot)
        {
            s.push(pRoot);
            pRoot = pRoot->pLChild;

        }
        else
        {
            if (s.size() == 0)
                break;

            pRoot = s.top();
            s.pop();
            cout << pRoot->value << endl;
            pRoot = pRoot->pRChild;
        }
    }
}

 

 

非递归——后序遍历:

左——右——根
和前序中序基本相同
区别在于如何判断从栈顶的一个结点的右结点是否被处理过
可以添加一个指针, 指向上一次弹出的结点, 如果这个结点和栈顶的右结点相同, 则代表右结点处理过了。
void LRD_() // 非递归后序遍历
{
    if (rbt == nullptr)
        return ;
    stack<Node *> s;
    Node *pRoot = rbt;
    Node *pLast = NULL;

    while (1)
    {
        if (pRoot != nullptr)
        {
            s.push(pRoot);
            pRoot = pRoot->pLChild;
        }
        else
        {
            if (s.size() == 0)
                break;
            pRoot = s.top();

            if (pRoot->pRChild != nullptr)
            {
                if (pLast != pRoot->pRChild)
                {
                    pRoot = pRoot->pRChild;
                }
                else
                {
                    if (s.size() == 0)
                        break;
                    s.pop();
                    pLast = pRoot;
                    cout << pRoot->value << endl;
                    pRoot = nullptr;
                }
            }
            else
            {
                if (s.size() == 0)
                    break;
                s.pop();
                pLast = pRoot;
                cout << pRoot->value << endl;
                pRoot = nullptr;

            }
        }
    }

}

 

 

广度遍历:

使用队列
将根结点添加到队列中
while (栈非空)
{
  取出结点
  将结点的左结点放入栈中
  将结点的右结点放入栈中
} 
 

题:将每层的结点打印在一层上:

方法1:
每次取出标记结点时, 再次将标记结点放入队列
将根节点放入队列中
将NULL作为换行标志放入队列中
while (队列非空)
{
  取出结点
  if (是空结点)
    if (队列为空)
      break;
    将空结点再次进入队列
    打印换行
    continue
  将结点的左结点放入栈中
  将结点的右结点放入栈中
 
}
 
方法2:
使用两个指针标记当前行的末尾和下一行的末尾
当前行标记根
每入队一个, 下一行末尾更新一次
如果当前行的最后一个出队了, 代表下一行已经全部入队
此时输出换行符, 将下一行赋值给当前行再次处理即可
 
void Sequence_Traversal() // 两个指针, 进行层序遍历, 每一层在一行
{
    if (rbt == NULL)
        return ;

    queue<Node *> q; // 用于广度遍历
    Node *pCurrent = nullptr;
    Node *pNext = nullptr;

    q.push(rbt);
    pCurrent = rbt;

    while (q.size())
    {
        Node *pNode = q.front();            

        q.pop();

        cout << pNode->value << ' ';

        if (pNode->pLChild != NULL)
        {
            q.push(pNode->pLChild);
            pNext = pNode->pLChild;
        }
        if (pNode->pRChild != NULL)
        {
            q.push(pNode->pRChild);
            pNext = pNode->pRChild;
        }

        if (pCurrent == pNode)
        {
            pCurrent = pNext;
            cout << endl;
        }
    }
}

 

 
方法3:
使用两个队列
将当前行放入一个队列中
下一行放入另外一个队列中
如果当前行空了, 换行
交换当前行的队列和下一行的队列处理
 
方法4:
两个队列
根入队, 另外一个队列入队1
两个队列同时出队
将当前出队的结点的子结点入队, 子结点一定在下一层, 所以入队当前结点层数+1
如果当前出队的层数和上一个不相同, 输出换行

 

posted @ 2020-08-14 12:57  x_Aaron  阅读(99)  评论(0)    收藏  举报