二叉树入门个人总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 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);
	}
}





猜你喜欢

转载自blog.csdn.net/Flynn_curry/article/details/63263189
今日推荐