勿以烂小而不摆|

notyour_young

园龄:3年3个月粉丝:10关注:1

二叉搜索树(静态实现)

模板


储存

struct node
{
	int v,l,r;
}tr[N];
//优点:节省空间,题给多少节点,就设多少节点;
//缺点:难以维护与完全二叉树有关的问题...
int tr[N];//左儿子:pos << 1,右儿子:pos << 1 | 1;
//优点:可判断完全二叉树的问题,易于维护节点与祖先节点关系问题
//缺点:空间利用率低,浪费空间,遇到左偏或右偏树,数据一大就会爆

插入

void insert(int & pos,int x)//普通版本
{
	if(pos == -1)
	{
		pos = idx;
		tr[idx ++] = {x,-1,-1};
		return ;
	}
	if(x < tr[pos].v) insert(tr[pos].l,x);
	else insert(tr[pos].r,x);
}
int p[N];
void insert(int & pos,int x,int last)//需维护每个节点的父节点的版本
{
	if(pos == -1)
	{
		pos = idx;
		tr[idx ++] = {x,-1,-1};
		p[pos] = last;
		return ;
	}
	if(x < tr[pos].v) insert(tr[pos].l,x);
	else insert(tr[pos].r,x);
}

void insert(int pos,int x)
{
	if(tr[pos] == -1) 
	{
		tr[pos] = x;
		return ;
	}
	if(x < tr[pos]) insert(pos << 1,x);
	else insert(pos << 1 | 1,x);
}

查找

int find(int pos,int x)//查找节点值为x的节点下标
{
	if(pos == -1) return -1;
	else if(tr[pos].v == x) return pos;
	else if(x < tr[pos].v) return find(tr[pos].l,x);
	else return find(tr[pos]l.r,x);
}

int find(int pos,int x)
{
	if(tr[pos] == -1) return -1;
	else if(tr[pos] == x) return pos;
	else if(x < tr[pos]) return find(pos << 1,x);
	else return find(pos << 1 | 1,x);
}

删除

void del(int u)
{
	if((tr[u].l == 0 || st[tr[u].l]) && (tr[u].r == 0 || st[tr[u].r]))
		return st[u] = true, void();
	else if(tr[u].l && !st[tr[u].l])
	{
		int p = tr[u].l;
		while(tr[p].r && !st[tr[p].r]) 
			p = tr[p].r;
		tr[u].v = tr[p].v;
		pos[tr[u].v] = u;
		del(p);
	}
	else if(tr[u].r && !st[tr[u].r])
	{
		int p = tr[u].r;
		while(tr[p].l && !st[tr[p].l])
			p = tr[p].l;
		tr[u].v = tr[p].v;
		pos[tr[u].v] = u;
		del(p);
	}
}

遍历、获取深度,求高度等模板与二叉树相同,不再赘述

经验

(1) 二叉搜索树的中序遍历序列一定是有序的。
(2) 对于组成元素相同而顺序不同的序列是有可能构成同样的一棵二叉搜索树的,而对于这些序列来说,二叉搜索树的前序遍历序列是这些序列中字典序最小的序列

例题

L3-010 是否完全二叉搜索树 (30 分)
将一系列给定数字顺序插入一个初始为空的二叉搜索树(定义为左子树键值大,右子树键值小),你需要判断最后的树是否一棵完全二叉树,并且给出其层序遍历的结果。

输入格式
输入第一行给出一个不超过20的正整数N;第二行给出N个互不相同的正整数,其间以空格分隔。

输出格式
将输入的N个正整数顺序插入一个初始为空的二叉搜索树。在第一行中输出结果树的层序遍历结果,数字间以1个空格分隔,行的首尾不得有多余空格。第二行输出YES,如果该树是完全二叉树;否则输出NO。

输入样例1:

9
38 45 42 24 58 30 67 12 51

输出样例1:

38 45 24 58 42 30 12 67 51
YES

输入样例2:

8
38 24 12 45 58 67 42 51

输出样例2:

38 45 24 58 42 12 67 51
NO

分析
这道题建树、遍历很简单,难在如何判断是否为一颗完全二叉树,以下是完全二叉树的定义

一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编
号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树
称为完全二叉树。

所以当我们用第二种建树方式时,只需遍历数组看1 ~ n的节点是否有空节点即可
ac代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<stack>
#include<set>
#include<deque>
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl '\n'
#define pi 3.14159265358979323846
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
 
const int N = 1000010,INF = 0x3f3f3f3f,mod = 998244353;
const double INFF = 0x7f7f7f7f7f7f7f7f;

int tr[N];
int n,m;

void insert(int pos,int x)
{
	if(tr[pos] == -1) 
	{
        m = max(pos,m);
		tr[pos] = x;
		return ;
	}
	if(x >= tr[pos]) insert(pos << 1,x);
	else insert(pos << 1 | 1,x);
}

vector<int> bfs()
{
	vector<int> res;
	queue<int> q;
	q.push(1);
	while(q.size())
	{
		int t = q.front();
		q.pop();
		res.push_back(tr[t]);
		if(tr[t << 1] != -1) q.push(t << 1);
		if(tr[t << 1 | 1] != -1) q.push(t << 1 | 1);
	}
	return res;
}

bool check()
{
	for(int i = 1;i <= n;i ++)
    {
        if(tr[i] == -1) return false; 
    }
    return true;
}

int main()
{
	//ios;
	memset(tr,-1,sizeof tr);
	cin >> n;
	for(int i = 0;i < n;i ++)
	{
		int x;
		cin >> x;
		insert(1,x);
	}

	auto ans = bfs();
	cout << ans[0];
	for(int i = 1;i < ans.size();i ++) cout << ' ' << ans[i];
	puts("");
	
	if(check()) puts("YES");
	else puts("NO");

	return 0;
}

L3-016 二叉搜索树的结构 (30 分)
二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树。(摘自百度百科)

给定一系列互不相等的整数,将它们顺次插入一棵初始为空的二叉搜索树,然后对结果树的结构进行描述。你需要能判断给定的描述是否正确。例如将{ 2 4 1 3 0 }插入后,得到一棵二叉搜索树,则陈述句如“2是树的根”、“1和4是兄弟结点”、“3和0在同一层上”(指自顶向下的深度相同)、“2是4的双亲结点”、“3是4的左孩子”都是正确的;而“4是2的左孩子”、“1和3是兄弟结点”都是不正确的。

输入格式:
输入在第一行给出一个正整数N(≤100),随后一行给出N个互不相同的整数,数字间以空格分隔,要求将之顺次插入一棵初始为空的二叉搜索树。之后给出一个正整数M(≤100),随后M行,每行给出一句待判断的陈述句。陈述句有以下6种:

A is the root,即"A是树的根";
A and B are siblings,即"A和B是兄弟结点";
A is the parent of B,即"A是B的双亲结点";
A is the left child of B,即"A是B的左孩子";
A is the right child of B,即"A是B的右孩子";
A and B are on the same level,即"A和B在同一层上"。
题目保证所有给定的整数都在整型范围内。

输出格式:
对每句陈述,如果正确则输出Yes,否则输出No,每句占一行。

输入样例:

5
2 4 1 3 0
8
2 is the root
1 and 4 are siblings
3 and 0 are on the same level
2 is the parent of 4
3 is the left child of 4
1 is the right child of 2
4 and 0 are on the same level
100 is the right child of 3

输出样例:

Yes
Yes
Yes
Yes
Yes
No
No
No

经典判断关系题
ac代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<stack>
#include<set>
#include<deque>
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl '\n'
#define pi 3.14159265358979323846
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
 
const int N = 110,INF = 0x3f3f3f3f,mod = 998244353;
const double INFF = 0x7f7f7f7f7f7f7f7f;

struct node
{
	int v,l,r;
}tr[N];
int idx,root = -1;
int n;
int p[N] = {-1};
void insert(int & pos,int x,int last)
{
	if(pos == -1)
	{
		pos = idx;
		tr[idx ++] = {x,-1,-1};
		p[pos] = last;
		return ;
	}
	if(x < tr[pos].v) insert(tr[pos].l,x,pos);
	else insert(tr[pos].r,x,pos);
}

int find(int pos,int x)
{
	if(pos == -1) return -1;
	else if(x == tr[pos].v) return pos;
	else if(x < tr[pos].v) return find(tr[pos].l,x);
	else return find(tr[pos].r,x);
}

int get_depth(int u)
{
	int res = 0;
	while(u != -1)
	{
		res ++;
		u = p[u];
	}
	return res;
}

int main()
{
	ios;
	cin >> n;
	while(n --)
	{
		int x;
		cin >> x;
		insert(root,x,-1);
	}
	cin >> n;
	while(n --)
	{
		int a,b;
		string s;
		cin >> a >> s;
		if(s == "is")
		{
			cin >> s >> s;
			if(s == "root")
			{
				if(tr[0].v == a) cout << "Yes" << endl;
				else cout << "No" << endl;
			}
			else if(s == "left")
			{
				cin >> s >> s >> b;
				b = find(root,b);
                if(b == -1) 
                {
                    cout << "No" << endl;
                    continue;
                }
				if(tr[tr[b].l].v == a) cout << "Yes" << endl;
				else cout << "No" << endl;

			}
			else if(s == "right")
			{
				cin >> s >> s >> b;
				b = find(root,b);
                if(b == -1) 
                {
                    cout << "No" << endl;
                    continue;
                }
				if(tr[tr[b].r].v == a) cout << "Yes" << endl;
				else cout << "No" << endl;
			}
			else
			{
				cin >> s >> b;
                b = find(root,b);
                if(b == -1) 
                {
                    cout << "No" << endl;
                    continue;
                }
				if(tr[p[b]].v == a) cout << "Yes" << endl;
				else cout << "No" << endl;
			}
		}
		else
		{
			cin >> b >> s >> s;
            a = find(root,a),b = find(root,b);
            if(a == -1 || b == -1)
            {
                cout << "No" << endl;
                continue;
            }
			if(s == "siblings")
			{
				if(p[a] == p[b]) cout << "Yes" << endl;
				else cout << "No" << endl;
			}
			else
			{
				if(get_depth(a) == get_depth(b)) cout << "Yes" << endl;
				else cout << "No" << endl;
				cin >> s >> s >> s;
			}
		}
	}
	return 0;
}

本文作者:notyour_young

本文链接:https://www.cnblogs.com/notyour-young/p/16218473.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   notyour_young  阅读(111)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起