版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Flynn_curry/article/details/63263189
二叉树是pat高频考点,最近做了几道入了个门,简单总结下。(水平有限,大神绕道)
一、二叉树
最典型的就是已知前序中序建树或已知后序中序建树,接着再后序或前序或层序遍历。难点主要在于建树。代表题目有hdu1710和 gplt L2-006。
不管是前序还是后序,都是通过遍历所有根,再用中序扩充根节点的方式建树。
已知前序中序建树:
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 1005;
const int INF = 0x3f3f3f3f;
typedef struct Tree
{
Tree *left;
Tree *right;
int val;
}Tree;
Tree *root;
int cnt, n;
Tree* build(int *preorder, int *inorder, int len)
{
Tree *tmp;
for(int i = 0; i < len; i++)
{
if(preorder[0]==inorder[i])//在中序中找到这个根
{
tmp = (Tree*)malloc(sizeof(Tree));
tmp->val = inorder[i];
tmp->left = build(preorder+1, inorder, i);
tmp->right = build(preorder+i+1, inorder+i+1, len-(i+1));//右子树
return tmp;//记得返回
}
}
return NULL;//没找到则返回NULL
}
已知后序中序建树:
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
const int N = 1005;
const int INF = 0x3f3f3f3f;
typedef struct Tree
{
Tree *left;
Tree *right;
int val;
}Tree;
Tree *root;
int cnt, n;
Tree* build(int *postorder, int *inorder, int len)//len代表右子树长度
{
Tree *tmp;
for(int i = 0; i < len; i++)
{
if(postorder[n-1]==inorder[i])
{
tmp = (Tree*)malloc(sizeof(Tree));
tmp->val = inorder[i];
tmp->left = build(postorder-(len-i), inorder, i);
tmp->right = build(postorder-1, inorder+i+1, len-(i+1));//右子树
return tmp;//记得返回
}
}
return NULL;//没找到返回空
}
我们可以看出主要区别就在于左右子树前序/后序下标的变化:
前序是从左往右找根,后序是从右往左找根。
前序的左子树从preorder+1开始,后序的右子树从postorder-1开始;
前序的右子树从preorder+i+1开始,后序的右子树从postorder-(len-i)开始。
二、二叉搜索树(BST)
BST通常指左边比根小、右边比根大的二叉树,不过也有左大右小的树,所以衍生出镜像问题。
主要问题有几类:
(1)、给你两个串,每个串构成一棵BST,判断是否是同一棵树;hdu3791
(2)、给你一个串,构成一棵BST,输出最小字典序序列;hdu3999
(3)、判断一个序列是否是BST或其镜像前序遍历的结果。gplt L2-004
同样,几乎所有问题都围绕着建树过程,这个过程必须非常熟悉。
建树过程:
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
const int N = 1005;
const int INF = 0x3f3f3f3f;
typedef struct Tree
{
Tree *left;
Tree *right;
int val;
}Tree;
Tree *root;
int cnt, n;
Tree* creat(int num)//创建新节点
{
Tree *node = (Tree*)malloc(sizeof(Tree));
node->left = NULL;
node->right = NULL;
node->val = num;
return node;
}
Tree* insert(Tree *node, int num)
{
if(node == NULL) node = creat(num);
else
{
if(num<node->val) node->left = insert(node->left, num);//前面记得是给左右节点赋值
else if(num>node->val) node->right = insert(node->right, num);
}
return node;
}
void build(int *seq)
{
root = NULL;//根记得变为NULL
for(int i = 0; i < n; i++)
root = insert(root, seq[i]);
}
具体哪道题怎么做就不详述了,不过还是想提一下如何判断一个序列是前序遍历的结果:gplt L2-004
//假设为左小右大BST
bool check(int l, int r)//l为树的最左节点,r为树的最右节点
{
if(l>=r) return true;
int i;
for(i = l+1; i <= r; i++)//找到右子树第一个节点
{
if(seq[i]>seq[l]) break;
}
for(int j = i; j <= r; j++)
{
if(seq[j]<seq[l]) return false;
}
return check(l+1, i-1)&&check(i, r);
}
不熟练的话多看一看。
还有些衍生问题比如判断完全BST,这都是在建好树的基础上进行的一系列简单操作,就不多述了。
最后是前序后序层序遍历:
前序:
void preorder(Tree *cur)
{
if(cur != NULL)
{
if(cnt == n-1) printf("%d\n", cur->val);
else
{
printf("%d ", cur->val);
cnt++;
}
preorder(cur->left);
preorder(cur->right);
}
}
后序:
void postorder(Tree *cur)
{
if(cur != NULL)
{
postorder(cur->left);
postorder(cur->right);
if(cnt == n-1) printf("%d\n", cur->val);
else
{
printf("%d ", cur->val);
cnt++;
}
}
}
层序(bfs):
void levelorder(Tree *cur)
{
Tree *tmp;
queue<Tree*>que;
while(!que.empty()) que.pop();
que.push(cur);
while(!que.empty())
{
tmp = que.front();
que.pop();
if(cnt == n-1) printf("%d\n", tmp->val);
else
{
printf("%d ", tmp->val);
cnt++;
}
if(tmp->left != NULL) que.push(tmp->left);
if(tmp->right != NULL) que.push(tmp->right);
}
}