二叉樹入門個人總結
阿新 • • 發佈:2019-01-24
二叉樹是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);
}
}