根据 后序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