1. 程式人生 > >二叉樹入門個人總結

二叉樹入門個人總結

二叉樹是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);
	}
}