二叉树的构造和遍历

二叉树的构造

// 二叉树节点定义
typedef struct BNode
{
    char data;
    BNode *left;
    BNode *right;
} BNode, *BTree;

// 创建二叉树
int creatBTree(BTree &tree)
{
    char data;
    cin >> data;
    if (data == '#') // 使用#表示空节点
        tree = NULL;
    else
    {
        tree = new BNode; // 创建根节点
        tree->data = data;
        creatBTree(tree->left); // 创建左子树
        creatBTree(tree->right);// 创建右子树
    }
    return 0;
}
// 删除二叉树
int destoryBTree(BTree tree)
{
    // 
    if (tree)
    {
        BTree left = tree->left;
        BTree right = tree->right;
        delete tree;
        if (left)
            destoryBTree(left);
        if (right)
            destoryBTree(right);
    }
    //if (tree)
    //{
    //  if (tree->left)
    //      destoryBTree(tree->left);
    //  if (tree->right)
    //      destoryBTree(tree->right);
    //  delete tree;
    //}
    return 0;
}

二叉树的遍历

// 访问二叉树
void visit(BTree tree)
{
    if (tree)
        cout << tree->data << " ";
}

二叉树的前序遍历

// 递归方式
void preOrder(BTree tree)
{
    if (tree) {
        visit(tree);
        preOrder(tree->left);
        preOrder(tree->right);
    }
}
// 前序遍历的非递归版本
// 分析过程
/*
分析:
“右子树之间”的访问顺序呈现栈规律即“先入栈后访问”

前序遍历:
1 优先访问根节点和左子树,其右子树的根节点入栈暂存
2 右子树的根节点出栈后的按照1的顺序访问
*/
void preOrder2(BTree tree)
{
    stack<BTree> sTree;
    while (tree || !sTree.empty() )
    {
        if (tree)
        {
            // 访问根节点
            visit(tree);         

            // 根节点的右子节点入栈保存
            if (tree->right)
                sTree.push(tree->right);
            // 优先访问左子节点
            tree = tree->left;
        }
        else
        {
            tree = sTree.top();
            sTree.pop();
        }
    }
}
//先序遍历(非递归)
//访问T->data后,将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。
void PreOrder3(BTree tree) {
{
    stack<BTree> stack;
    while (tree || !stack.empty()) {   //栈不空或者p不空时循环
        if (tree) {
            stack.push(tree);          //存入栈中
            visit(tree);               //访问根节点
            tree = tree->left;       //遍历左子树
        } else {
            p = stack.top();           //退栈
            stack.pop();
            tree = tree->right;       //访问右子树
        }
    }
}

二叉树的中序遍历

// 递归遍历
void midOrder(BTree tree)
{
    if (tree) {
        midOrder(tree->left);
        visit(tree);
        midOrder(tree->right);
    }
}
// 中序遍历的非递归版本
// 分析过程
/*
分析
根节点与左子树呈现栈的规律
不同根节点的右子树之间呈现栈的规律
注:把树划分成由只由左子树组成树好理解!!!

遍历
1 若根节点存在,根节点入栈
2 左子树入栈,直到左子树的根节点没有左子树(左节点)
3 左子树根节点出栈并访问,使用右子树的根节点更新原根节点信息,循环执行1、2、3
*/
void midOrder2(BTree tree)
{
    stack<BTree> sTree;
    while (tree || !sTree.empty())
    {
        if (tree)
        {
            sTree.push(tree);
            tree = tree->left;
        }
        else
        {
            tree = sTree.top();
            sTree.pop();
            visit(tree);
            tree = tree->right;
        }

    }
}

二叉树的后序遍历

// 递归遍历
void postOrder(BTree tree)
{
    if (tree)
    {
        postOrder(tree->left);
        postOrder(tree->right);
        visit(tree);
    }
}
// 后序遍历
/*
<思路一>
分析:
1 将tree节点入栈,然后沿着左子树一直搜索,直到搜索到没有左孩子的节点;
2 这个节点出现在栈顶,但不能出栈并访问它,其有孩子还没有被访问。
3 按照 1、2的规则对其右子树进行处理,这个节点再次出现在栈顶时,出栈并访问。

注:整个过程中,每个节点都两次出现在栈顶,节点第二次出现在栈顶时才能访问它,可以设置一个bool值来标识。
*/
typedef struct BNodePost
{
    BTree bTree;
    bool isFirst;
}BNodePost, *BTreePost;

void postOrder2(BTree tree)
{
    stack<BTreePost> sBTreePost;
    BTreePost bTreePost;
    while (tree || !sBTreePost.empty())
    {
        if (tree)
        {
            // 沿左子树一直搜索并压栈,知道没有左子树
            BTreePost p = new BNodePost();
            p->bTree = tree;
            p->isFirst = true;
            sBTreePost.push(p);
            tree = tree->left;  
        }
        else
        {
            bTreePost = sBTreePost.top();
            sBTreePost.pop();
            if (bTreePost->isFirst) // 第一次出现在栈顶
            {
                bTreePost->isFirst = false;
                sBTreePost.push(bTreePost);
                tree = bTreePost->bTree->right;
            }
            else // 第二次出现在栈顶
            {
                visit(bTreePost->bTree);
                delete bTreePost;
            }               
        }   
    }
}
/*
<思路二>
分析:
后续遍历特点:根节点在其左孩子有孩子被访问过之后才能被访问
1 tree节点入栈
    1.1 此节点不存在左孩子右孩子,可以直接访问它
    1.2 此节点存在左孩子或右孩子,但其左孩子或右孩子节点已被访问过,可以直接访问该节点
2 非1.1、1.2情况,将tree节点右孩子先入栈,左孩子再入栈,这样来保证每次取栈顶元素的顺序即先访问左孩子再访问右孩子。 
*/
void postOrder3(BTree tree)
{
    stack<BTree> sBTree;
    BTree preTree = NULL; // 上一次被访问的节点

    if (tree)
        sBTree.push(tree);

    while (!sBTree.empty())
    {
        BTree curTree = sBTree.top(); // 当前节点
        if ( (!curTree->left && !curTree->right)
            || (preTree && (preTree == curTree->left|| preTree == curTree->right)) 
            )
        {
            visit(curTree);
            sBTree.pop();
            preTree = curTree;
        }
        else
        {
            if (curTree->right)
                sBTree.push(curTree->right);
            if (curTree->left)
                sBTree.push(curTree->left);
        }
    }
}

二叉树的层次遍历

// 层次遍历:典型的BFS
/*
分析过程:
层次遍历以树的高度来划分遍历的层次。
其中,层次遍历访问的方式为二叉树存储的方式
即:1 2*1 2*1+1 2*2 2*2+1 2*(2*1+1) 2*(2*1+1)+1 ....
那么层次遍历就是按二叉树节点存储位置顺序访问即可
根-左子节点-右子节点
*/

/* 
执行过程:
1 根节点入栈
2 根节点出栈,访问根节点
3 根节点的左右子节点入栈
4 重复2、3操作
*/
void levelOrder(BTree tree)
{
    queue<BTree> qTree;
    if (tree)
    {
        qTree.push(tree);
    }
    while (!qTree.empty())
    {
        // 根节点出队,访问根节点
        BTree curTree = qTree.front();
        qTree.pop();
        visit(curTree);

        // 左子节点不为空,入队
        if (curTree->left)
            qTree.push(curTree->left);
        // 右子节点不为空,入队
        if (curTree->right)
            qTree.push(curTree->right);

    }
}

二叉树的输入与删除

// 输入与输出
//ABC##DE#G##F###
//先序遍历        :A B C D E G F
//先序遍历(非递归):A B C D E G F
//
//中序遍历        :C B E G D F A
//中序遍历(非递归):C B E G D F A
//
//后序遍历        :C G E F D B A
//后序遍历(非递归):C G E F D B A
//
//层次遍历        :A B C D E F G
//

///       A
///      /
///     B
///    / \
///   C   D
///      / \
///     E   F
///      \
///       G

参考

1 赵4老师版本
2 二叉树的非递归遍历

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值