让我们先理解一下什么叫做二叉查找树;
注意:二叉查找树的中序遍历序列总是呈现递增规律。
我们也可以通过 某棵树的的中序遍历序列是否呈现递增规律 来 判断 该树是否为 BST.
1.BST的建立
构建BST的过程就是将一系列互不相同的正整数依次插入到一棵初始为空的树中。
每次插入时,从根节点开始比较,根据大小关系决定插入到左子树还是右子树,直到找到合适的位置。
时空复杂度
-
时间复杂度:
- 插入操作的时间复杂度为O(h),其中h是树的高度。在最坏情况下(例如,插入的序列是严格递增或递减的),树会退化成链表,高度为n,因此插入操作的总时间复杂度为O(n^2)。
- 先序遍历的时间复杂度为O(n),因为每个节点都会被访问一次。
-
空间复杂度:
- 主要的空间开销来自于递归栈和存储节点的数组。在最坏情况下,递归栈的深度为n,因此空间复杂度为O(n)。
核心代码:
int newNode(int data)
{
nodes[nodeCount].data = data;//按顺序存储结点数据
nodes[nodeCount].l = nodes[nodeCount].r = -1;
return nodeCount++;
}int insert(int root, int data)
{
//找到合适的空位置,开始创建二叉查找树上的新结点
if (root == -1)
return newNode(data);if (data < nodes[root].data)
{
//递归创建BST
nodes[root].l = insert(nodes[root].l, data);
}
else
{
nodes[root].r = insert(nodes[root].r, data);
}return root;
}
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 50;
struct Node
{
int data;
int r,l;
}nodes[MAXN];
int nodeCount = 0;
int newNode(int data)
{
nodes[nodeCount].data = data;//按顺序存储结点数据
nodes[nodeCount].l = nodes[nodeCount].r = -1;
return nodeCount++;
}
int insert(int root, int data)
{
//找到合适的空位置,开始创建二叉查找树上的新结点
if (root == -1)
return newNode(data);
if (data < nodes[root].data)
{
//递归创建BST
nodes[root].l = insert(nodes[root].l, data);
}
else
{
nodes[root].r = insert(nodes[root].r, data);
}
return root;
}
vector<int> pre;
void preOrder(int root)
{
if (root == -1)return;
pre.push_back(nodes[root].data);
preOrder(nodes[root].l);
preOrder(nodes[root].r);
}
int main()
{
int n, data, root = -1;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &data);
root = insert(root, data);//其实每次都会返回根结点
}
preOrder(root);
for (int i = 0; i < pre.size(); i++)
{
printf("%d", pre[i]);
if (i < (int)pre.size() - 1)
printf(" ");
}
return 0;
}
2.二叉查找树的判定
注意:二叉查找树的中序遍历序列总是呈现递增规律。
因此在二叉查找树(Binary Search Tree, BST)中,中序遍历的结果是一个严格递增的序列。
因此,验证一个给定的中序遍历序列是否来自一个二叉查找树,只需检查该序列是否严格递增。
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
using namespace std;
vector<int> in;
bool isBST()
{
for (int i = 1; i < in.size(); i++)
{
if (in[i] <= in[i - 1])
return false;
}
return true;
}
int main()
{
int n, x;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &x);
in.push_back(x);
}
//输出结果
printf(isBST() ? "Yes" : "No");
return 0;
}
3.还原二叉查找树
不能由一个先序遍历序列确定唯一一棵BST,
但是由6.先序中序还原二叉树知,可以通过一个先序遍历序列和一个中序遍历序列确定唯一一棵二叉树,(或是中序+后序,但前序和后序不行。因为前序和后序在本质上都是将父节点与子结点进行分离,但并没有指明左子树和右子树的能力,因此得到这两个序列只能明确父子关系,而不能确定一个二叉树)
题目已给出一个先序遍历序列,而对于二叉查找树,中序遍历序列是一个有序序列,我们可以通过将先序遍历序列排序得到中序遍历序列。
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 50;
struct Node
{
int data;
int r, l;
}nodes[MAXN];
int nodeCount = 0;
int newNode(int data)
{
nodes[nodeCount].data = data;//按顺序存储结点数据
nodes[nodeCount].l = nodes[nodeCount].r = -1;
return nodeCount++;
}
vector<int> pre,in,post;
int buildTree(int preL, int preR, int inL, int inR)
{
if(preL>preR)
{
return -1;
}
int root = newNode(pre[preL]);//创建根结点
int inIndexOfRoot;
for (int i = inL; i <= inR; i++)
{
if (in[i] == nodes[root].data)
{
inIndexOfRoot = i;
break;
}
}
int leftCount = inIndexOfRoot - inL;//左子树结点数
nodes[root].l = buildTree(preL + 1, preL + leftCount, inL, inIndexOfRoot - 1);//构建左子树
nodes[root].r = buildTree(preL + leftCount + 1, preR, inIndexOfRoot + 1, inR);//构建右子树
return root;
}
void postOrder(int root)
{
if (root == -1)return;
postOrder(nodes[root].l);
postOrder(nodes[root].r);
post.push_back(nodes[root].data);
}
int main()
{
int n,x;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &x);
pre.push_back(x);
}
//获得中序遍历序列
in = pre;
sort(in.begin(), in.end());
int root = buildTree(0, n - 1, 0, n - 1);
postOrder(root);
for (int i = 0; i < post.size(); i++)
{
printf("%d", post[i]);
if (i < (int)post.size() - 1)
printf(" ");
}
return 0;
}
4.相同的二叉查找树
由题3,我们知道,得知一个BST的先序遍历序列,自然可以知晓它的中序遍历序列。
因此,要判断两棵二叉查找树是否相同,可以通过比较它们的前序遍历序列来实现。
前序遍历序列能够唯一地确定一棵二叉查找树的结构。
(我个人认为后序遍历序列也是可以的,实际上用后序也的确可以通过)
代码如下(先序):
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 50*2;
struct Node
{
int data;
int r, l;
}nodes[MAXN];
int nodeCount = 0;
int newNode(int data)
{
nodes[nodeCount].data = data;//按顺序存储结点数据
nodes[nodeCount].l = nodes[nodeCount].r = -1;
return nodeCount++;
}
int insert(int root, int data)
{
if (root == -1) return newNode(data);
if (data < nodes[root].data) nodes[root].l = insert(nodes[root].l, data);
else nodes[root].r = insert(nodes[root].r, data);
return root;
}
void preOrder(int root, vector<int>& pre) {
if (root == -1) {
return;
}
pre.push_back(nodes[root].data); // 访问根节点
preOrder(nodes[root].l, pre); // 遍历左子树
preOrder(nodes[root].r, pre); // 遍历右子树
}
int main()
{
int n, data;
scanf("%d", &n);
int root1 = -1, root2 = -1;
for (int i = 0; i < n; i++)
{
scanf("%d", &data);
root1 = insert(root1, data);
}
for (int i = 0; i < n; i++)
{
scanf("%d", &data);
root2 = insert(root2, data);
}
vector<int> pre1, pre2;
preOrder(root1, pre1);
preOrder(root2, pre2);
printf(pre1 == pre2 ? "Yes" : "No");
return 0;
}
5.填充二叉查找树
废话不多说,上代码
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 50*2;
vector<int> pre, in;
struct Node
{
int data;
int r, l;
}nodes[MAXN];
int inIdx=0;
void inOrder(int root)
{
if (root == -1)
return;
inOrder(nodes[root].l);
nodes[root].data = in[inIdx++];
inOrder(nodes[root].r);
}
void preOrder(int root)
{
if (root == -1) return;
pre.push_back(nodes[root].data);
preOrder(nodes[root].l);
preOrder(nodes[root].r);
}
int main()
{
int n, x;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &x);
in.push_back(x);
}
for (int i = 0; i < n; i++)
{
scanf("%d%d", &nodes[i].l, &nodes[i].r);
}
sort(in.begin(), in.end());
inOrder(0);
preOrder(0);
for (int i = 0; i < (int)pre.size(); i++)
{
printf("%d", pre[i]);
if (i < (int)pre.size() - 1)
printf(" ");
}
return 0;
}