二叉树、二叉搜索树

目录

一、新二叉树

二、二叉排序树/二叉搜索树

1.定义

2.查找x

3.插入x

4.建树

5.删除x

6.遍历

7.完整代码

8.性能


一、新二叉树

1.特点:每个结点最多有两个子树(结点的度不大于2),有左右之分。

2.性质:

  1. 第 i 层最多有 2i-1 个结点。
  2. 二叉树深度为 K,则最多有 2K-1 个结点。
  3. 终端结点数(叶子结点数)为 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为底)。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序和三三总有一个能跑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值