目录
一、新二叉树
1.特点:每个结点最多有两个子树(结点的度不大于2),有左右之分。
2.性质:
- 第 i 层最多有 2i-1 个结点。
- 二叉树深度为 K,则最多有 2K-1 个结点。
- 终端结点数(叶子结点数)为 a,度为 2 的结点数为 b,则 a=b+1;
3.一个二叉树的前序遍历题:P1305 新二叉树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include <bits/stdc++.h>
const int maxn=777;
const int mod=1e9+7;
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
char c,cc;
struct Root
{
char lc;//left child
char rc;//right child
}node[130];
//后序是左右根,中序是左根右
void Tree(char x)//前序遍历就是根节点的访问顺序为:根左右
{
if(x=='*')//遇到空节点
return;
cout<<x;
Tree(node[x].lc);//找到他的左孩子,继续往下探(如果左孩子是*的话,会返回的)
Tree(node[x].rc);
}
int main()
{
int n;
cin>>n;
cin>>cc;//第一个字母单独处理,记录下来是为了作为递归的开始
cin>>node[cc].lc;
cin>>node[cc].rc;
for(int i=2;i<=n;i++)
{
cin>>c;
cin>>node[c].lc;
cin>>node[c].rc;
}
Tree(cc);
}
二、二叉排序树/二叉搜索树
1.定义
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
2.查找x
如果根结点等于x,直接就找到了,否则若x小于根结点就递归查左子树,大于根结点就递归右子树,最后如果子树是空树,就说明找不到。
struct node
{
int data;//数据域
node *left,*right;//指针域
};
void search(node *root,int x)
{
if(root==NULL)//空树说明查找失败
{
printf("Search Failed\n");
return;
}
if(x==root->data)
printf("%d\n is here!\n");
else if(x<root->data)
search(root->left,x);
else
search(root->right,x);
}
3.插入x
- 空树就查找失败,也相当于找到了x的窝窝,直接让x当根结点
- 不是空就为x查找有关合理的位置,直到找到空子树
- 这一些主要是为了建树做的准备
4.建树
建树就是插入很多结点啦~
node* NewNode(int x)
{
node* Node=new node;//申请一个node型的地址空间
Node->data=x;
Node->left=Node->right=NULL;//刚开始是孤家寡人,哪里来的孩子
return Node;//返回值是新建结点的地址
}
void insert(node* &root,int x)
{
if(root==NULL)//找到空树,为x的坑
{
root=NewNode(x);
return;
}
if(x==root->data)
return;//查找成功,说明该结点已经存在,直接返回
else if(x<root->data)
insert(root->left,x);//在左子树上插入x
else
insert(root->right,x);
}
node *BuildTree(int data[],int n)
{
node* root=NULL;//先新建一个根结点
for(int i=0;i<n;i++)
{
insert(root,data[i]);
}
return root;//返回根结点
}
5.删除x
几个概念:
- 前驱、后继:以二叉査找树中比结点权值小的最大结点称为该结点的前驱,而把比结点权值大的最小结点称为该结点的后继。
- 前驱是结点子树里的最大值结点,它没有右子树(因为右孩子会更大,而前驱结点已经是最大),同理后继没有左子树。
步骤:
- 如果不存在x这个点,直接返回
- 如果找到了x这个点,且他没有孩子,说明要删除的是叶结点,直接删除就行;
***如果当前结点有左孩子,那么在左子树中寻找结点前驱,然后让的前驱数据覆盖结点,接着在左子树中删除前驱结点;
***如果当前结点有右孩子,那么在右子树中寻找结点后继,然后让后继的数据覆盖结点,接着在右子树中删除后继结点;
- 如果结点权值大于x,则继续在左子树中递归删除x,小于x则在右子树中递归删除。
//前驱,寻找以root为根结点的树中的最大权值结点 node* FindMax(node* root) { while(root->right!=NULL)//不断往右(权值大),直到没有右孩子 root=root->right; return root; } //后继,寻找以root为根结点的树中的最小权值结点 node* FindMin(node* root) { while(root->left!=NULL)//同理 ,后继一定没有左子树,因为左孩子会更小,而后继next已经是最小 root=root->left; return root; } void DeleteNode(node* &root,int x) { if(root==NULL)//根结点不存在,直接返回 return; if(root->data==x)//找到了结点 { if(root->left==NULL&&root->right==NULL)//x是叶子 root=NULL; else if(root->left!=NULL)//左子树 { node* pre=FindMax(root->left);//找到root的前驱即找最大结点 root->data=pre->data;//用前驱覆盖root DeleteNode(root->left,pre->data);//在左树中删除前驱结点 } else//右子树 { node* next=FindMax(root->right);//找到root的后继即找最小结点 root->data=next->data;//用后继覆盖root DeleteNode(root->right,next->data);//在左树中删除后继结点 } } else if(root->data>x) { DeleteNode(root->left,x);//x太小了,往左子树里再找找 } else { DeleteNode(root->right,x);//x太小了,往左子树里再找找 } }
6.遍历
递归实现的先序、中序、后序遍历:
其中中序遍历是递增顺序。
通过中序与前序的遍历结果或者中序与后序的遍历结果,即可从这些结果中得到唯一的二叉树。
确定方式:
1. 根据前序遍历的首元素或者后序遍历的尾元素,在中序遍历确定根节点。
2. 随后根据该根节点在中序遍历中确定左右子树。
//先序遍历,DLR
void PreOrder(node* root)
{
if(root!=NULL)
{
cout<<root->data;
PreOrder(root->left);
PreOrder(root->right);
}
}
//中序遍历,LDR
void InOrder(node* root)
{
if(root != NULL)
{
InOrder(root->left);
cout<<root->data;
InOrder(root->right);
}
}
//后序遍历,LRD
void PostOrder(node* root)
{
if(root != NULL)
{
PostOrder(root->left);
PostOrder(root->right);
cout<<root->data;
}
}
一个题:给出一棵二叉树的中序与后序排列。求出它的先序排列。
传送门:P1030 [NOIP2001 普及组] 求先序排列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cnk
看洛谷第一个题解写出来,画个图康康:
摘要:
1.后序遍历中,最后一个节点一定是根节点(对于每一颗子树也成立);
2.既然这题要求先序遍历,那么我们只需一次输出访问的父节点即可;
3.递归将一棵大树分成两颗子树,让后找他们的父节点,不断递归输出
#include <bits/stdc++.h>
const int maxn=777;
const int mod=1e9+7;
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
string middle,after;
void dfs(string m,string a)
{
int lena=a.length();
int lenm=m.length();
if(lenm>0)
{
cout<<a[lenm-1];
int index=m.find(a[lenm-1]);
//先递归左子树,再递归右子树,保证中左右
dfs(m.substr(0,index),a.substr(0,index));//从下标为0开始截取长度k
dfs(m.substr(index+1),a.substr(index,lenm-index-1)); //从下标k+1开始一直到结尾
}
}
int main()
{
cin>>middle>>after;
dfs(middle,after);
}
7.完整代码
#include <bits/stdc++.h>
const int maxn=777;
const int mod=1e9+7;
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
struct node
{
int data;//数据域
node *left,*right;//指针域
};
//查找数据域为x的结点
void search(node *root,int x)
{
if(root==NULL)//空树说明查找失败
{
printf("Search Failed\n");
return;
}
if(x==root->data)
printf("%d is here!\n");
else if(x<root->data)
search(root->left,x);
else
search(root->right,x);
}
node* NewNode(int x)
{
node* Node=new node;//申请一个node型的地址空间
Node->data=x;
Node->left=Node->right=NULL;//刚开始是孤家寡人,哪里来的孩子
return Node;//返回值是新建结点的地址
}
void insert(node* &root,int x)
{
if(root==NULL)//找到空树,为x的坑
{
root=NewNode(x);
return;
}
if(x==root->data)
return;//查找成功,说明该结点已经存在,直接返回
else if(x<root->data)
insert(root->left,x);//在左子树上插入x
else
insert(root->right,x);
}
node *BuildTree(int data[],int n)
{
node* root=NULL;//先新建一个根结点
for(int i=0;i<n;i++)
{
insert(root,data[i]);
}
return root;//返回根结点
}
//前驱,寻找以root为根结点的树中的最大权值结点
node* FindMax(node* root)
{
while(root->right!=NULL)//不断往右(权值大),直到没有右孩子
root=root->right;
return root;
}
//后继,寻找以root为根结点的树中的最小权值结点
node* FindMin(node* root)
{
while(root->left!=NULL)//同理 ,后继一定没有左子树,因为左孩子会更小,而后继next已经是最小
root=root->left;
return root;
}
void DeleteNode(node* &root,int x)
{
if(root==NULL)//根结点不存在,直接返回
return;
if(root->data==x)//找到了结点
{
if(root->left==NULL&&root->right==NULL)//x是叶子
root=NULL;
else if(root->left!=NULL)//左子树
{
node* pre=FindMax(root->left);//找到root的前驱即找最大结点
root->data=pre->data;//用前驱覆盖root
DeleteNode(root->left,pre->data);//在左树中删除前驱结点
}
else//右子树
{
node* next=FindMax(root->right);//找到root的后继即找最小结点
root->data=next->data;//用后继覆盖root
DeleteNode(root->right,next->data);//在左树中删除后继结点
}
}
else if(root->data>x)
{
DeleteNode(root->left,x);//x太小了,往左子树里再找找
}
else
{
DeleteNode(root->right,x);//x太小了,往左子树里再找找
}
}
//先序遍历,DLR
void PreOrder(node* root)
{
if(root!=NULL)
{
cout<<root->data;
PreOrder(root->left);
PreOrder(root->right);
}
}
//中序遍历,LDR
void InOrder(node* root)
{
if(root != NULL)
{
InOrder(root->left);
cout<<root->data;
InOrder(root->right);
}
}
//后序遍历,LRD
void PostOrder(node* root)
{
if(root != NULL)
{
PostOrder(root->left);
PostOrder(root->right);
cout<<root->data;
}
}
int main()
{
int n[maxn]={1,3,4,2,5};
//新建一个二叉树
node* Node=BuildTree(n,5);
PreOrder(Node);
cout<<endl;
InOrder(Node);
cout<<endl;
PostOrder(Node);
cout<<endl;
//查找
search(Node,6);
//插入操作
insert(Node,6);
search(Node,6);
PreOrder(Node);
cout<<endl;
//删除操作
DeleteNode(Node,6);
search(Node,6);
}
8.性能
最差的情况与顺序查找相同,ASL=(n+1)/2;
最好的情况与折半查找相同,ASL达到对数级logn(以2为底)。