根据后序或者前序 + 中序建树的多种方法!

9 篇文章 0 订阅
6 篇文章 0 订阅

根据 后序or前序 + 中序 建树的多种方法

以下例子都是实战题!值得收藏!值的学习!

1 堆的方式(完全二叉树)

  • 利用了完全二叉树的特性,根结点i的孩子结点左孩子为2i,右孩子为2i+1。(注意此时i是从1开始编号),但是每棵子树的根结点没有直接给出,而是在代码中通过循环查找出
// 团体程序设计竞赛 L2-006 树的遍历
#include <iostream>
using namespace std;
int N;
int Z[31];
int H[31];
int level[10000]; // 存储树

/*
    这个建树的方法直接采用的就是堆的形式。
*/
void createTree(int begin, int end, int pos)
{
    // 只要左子树或者右子树为空都会这样。
    if (begin > end)
    {
        return;
    }
    int separate = begin;
    int flag = 0; // 找到根结点后提前结束。
    
    // 这个是后序操作(从后往前),【如果是前序遍历则需要从前往后】
    for(int i = N-1;i>=0;i--)
    {
        for(int j =begin;j<=end;j++)
        {
            // 找后序遍历中,从后往前,最先出出现在中序遍历中的,就是根结点。
            
            if(H[i] == Z[j])
            {
                separate = j;
                flag = 1;
                break;
            }
        }
        if(flag == 1)
        {
            break;
        }
    }
    level[pos] = Z[separate]; 
    // begin end 表示的是在中序遍历中的起始和结尾点
    createTree(begin, separate - 1, 2 * pos + 1);
    createTree(separate+1, end, 2 * pos + 2);
}

int main()
{
    cin >> N;
    for (int i = 0; i < N; i++)
    {
        cin >> H[i];
    }
    for (int i = 0; i < N; i++)
    {
        cin >> Z[i];
    }
    createTree(0, N - 1, 0);
    // 这就是层序遍历,直接遍历数组
    for(int i = 0,count=0;count<N;i++)
    {
        if(count > 0 && level[i] != 0)
        {
            cout << " ";
        }
        if(level[i]!=0)
        {
            cout << level[i];
            count++;
        }
    }
    return 0;
}

2 链表方式(直接求出根结点位置)

  • 前序:左子树 (start, mid-1,root+1) 右子树 (mid+1, end, root + (mid-begin) + 1)
  • 后序:左子树 (start, mid-1,root - (end - mid ) - 1) 右子树 (mid+1, end, root - 1)
  • 关于链表可以直接返回二重指针(Tree),也可以直接传入Tree引用,直接改变跟根结点指针(void)。

2.1 以下是返回二重指针的后序+中序

// L2-3 浪漫侧影  2021年5月20号竞赛
#include <iostream>
#include <queue>
using namespace std;

int z[25];
int h[25];
int N;
int level[25];
int symbol[25];

typedef struct TNode
{
    int data;
    TNode *lchild, *rchild;
} TNode, *Tree;
Tree T;

// 求每个结点的层数
void Traverse(Tree T, int levelNum)
{
    if (T == NULL)
        return;
    level[T->data] = levelNum;
    Traverse(T->lchild, levelNum + 1);
    Traverse(T->rchild, levelNum + 1);
}

// 【核心代码】
Tree createrTree(int start, int end, int pos)
{
    if (start > end)
    {
        return NULL;
    }
    Tree t = new TNode;
    t->data = h[pos];
    int find = -1;
    // 找到根结点在此范围内的位置,再将区间一分为二。
    for (int i = start; i <= end; i++)
    {
        if (z[i] == h[pos])
        {
            find = i;
            break;
        }
    }
	
    
    // start和end存储的是中序中的位置
    // 为后序代码
    t->lchild = createrTree(start, find - 1, pos - (end - find) - 1);
    t->rchild = createrTree(find + 1, end, pos - 1);
    return t;
}


void levelTraverse()
{
    Tree temp;
    queue<Tree> q;
    q.push(T);
    cout << "R:";
    while (!q.empty())
    {
        temp = q.front();
        if (symbol[level[temp->data]] == 0)
        {
            symbol[level[temp->data]] = 1;
            cout << " " << temp->data;
        }
        q.pop();
        if (temp->rchild != NULL)
        {
            q.push(temp->rchild);
        }
        if (temp->lchild != NULL)
        {
            q.push(temp->lchild);
        }
    }
    cout << endl;
    q.push(T);
    fill(symbol, symbol + 25, 0);
    cout << "L:";
    while (!q.empty())
    {
        temp = q.front();
        if (symbol[level[temp->data]] == 0)
        {
            symbol[level[temp->data]] = 1;
            cout << " " << temp->data;
        }
        q.pop();
        if (temp->lchild != NULL)
        {
            q.push(temp->lchild);
        }
        if (temp->rchild != NULL)
        {
            q.push(temp->rchild);
        }
    }
}

int main()
{
    cin >> N;
    for (int i = 0; i < N; i++)
    {
        cin >> z[i];
    }
    for (int i = 0; i < N; i++)
    {
        cin >> h[i];
    }

    T = createrTree(0, N - 1, N - 1);
    Traverse(T, 1);
    levelTraverse();

    return 0;
}

2.2 以下是传入引用的前序+中序

// L2-011 玩转二叉树
/*
    这一题和二叉树的层序遍历那一题简直不要太像
    前序遍历的第一个就是根结点。
*/
#include <iostream>
#include <queue>
using namespace std;

int N;
int z[36];
int q[36];
int flag;
typedef struct BTNode
{
    int data;
    BTNode *lchild, *rchild;
} BTNode, *BTree;
void CreateTree(int begin, int end, int root, BTree &T)
{
    if (begin > end)
    {
        T = NULL;
        return;
    }
    BTree t = new BTNode;
    t->data = q[root];
    T = t;
    int pos = 0;
    // pos是根结点在中序结点中的位置。
    for (int i = begin; i <= end; i++)
    {
        if (z[i] == q[root])
        {
            pos = i;
            break;
        }
    }
    // being end 是表示在中序遍历的中的起始和终止索引位置。
    // 主要就是root怎么求?
    CreateTree(begin, pos - 1, root + 1, T->lchild); //左边的直接+1就是下一个root
    CreateTree(pos + 1, end, root + pos - begin + 1, T->rchild);//右边的话中间减去begin就是左子树的元素数+1,得到元素
    return;
}

// 层序遍历
void Leve_Order(BTree T)
{
	queue<BTNode*>q;
	q.push(T);
	while(!q.empty())
	{
		BTNode *p = q.front();
		q.pop();
		if(flag) cout<<' ';
		cout<<p->data;
		flag = 1;
		if(p->lchild)
			q.push(p->lchild);
		if(p->rchild)
			q.push(p->rchild);
	}
}

int main()
{
    cin >> N;
    for (int i = 0; i < N; i++)
    {
        cin >> z[i];
    }
    for (int i = 0; i < N; i++)
    {
        cin >> q[i];
    }
    BTree T = new BTNode;
    CreateTree(0, N - 1, 0, T);
    int countNum = 0;
    Leve_Order(T);
    return 0;
}

2.3 以下也是链表方式(后序+中序),但是对于根结点方式没有直接求,而是在代码中循环查找。

// L2-3 浪漫侧影  2021年5月20号竞赛
#include <iostream>
#include <queue>
using namespace std;

int z[25];
int h[25];
int N;
int level[25];
int symbol[25];

typedef struct TNode
{
    int data;
    TNode *lchild, *rchild;
} TNode, *Tree;
Tree T;
void Traverse(Tree T, int levelNum)
{
    if (T == NULL)
        return;
    level[T->data] = levelNum;
    Traverse(T->lchild, levelNum + 1);
    Traverse(T->rchild, levelNum + 1);
}

Tree createrTree(int start, int end)
{
    if (start > end)
    {
        return NULL;
    }
    Tree t = new TNode;

    int find = -1;

    for (int j = N - 1; j >= 0; j--)
    {
        // 中序的位置
        for (int i = start; i <= end; i++)
        {
            if (h[j] == z[i])
            {
                find = i;
                break;
            }
        }
        if (find != -1)
        {
            break;
        }
    }
    t->data = z[find];
    // 存储的是中序中的位置
    t->lchild = createrTree(start, find - 1);
    t->rchild = createrTree(find + 1, end);
    return t;
}

void levelTraverse()
{
    Tree temp;
    queue<Tree> q;
    q.push(T);
    cout << "R:";
    while (!q.empty())
    {
        temp = q.front();
        if (symbol[level[temp->data]] == 0)
        {
            symbol[level[temp->data]] = 1;
            cout << " " << temp->data;
        }
        q.pop();
        if (temp->rchild != NULL)
        {
            q.push(temp->rchild);
        }
        if (temp->lchild != NULL)
        {
            q.push(temp->lchild);
        }
    }
    cout << endl;
    q.push(T);
    fill(symbol, symbol + 25, 0);
    cout << "L:";
    while (!q.empty())
    {
        temp = q.front();
        if (symbol[level[temp->data]] == 0)
        {
            symbol[level[temp->data]] = 1;
            cout << " " << temp->data;
        }
        q.pop();
        if (temp->lchild != NULL)
        {
            q.push(temp->lchild);
        }
        if (temp->rchild != NULL)
        {
            q.push(temp->rchild);
        }
    }
}

int main()
{
    cin >> N;
    for (int i = 0; i < N; i++)
    {
        cin >> z[i];
    }
    for (int i = 0; i < N; i++)
    {
        cin >> h[i];
    }

    T = createrTree(0, N - 1);
    Traverse(T, 1);
    levelTraverse();

    return 0;
}

看到这里,对于后序or前序 + 中序构造树是不是非常清晰明了了呢!一起加油吧,同志! —— Jeff

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值