第十二章二叉查找树

二叉查找树的查找(查找某个值、查找最小元素、查找最大元素)、插入、删除操作的最坏时间都为O(h)。构造二叉查找树时采取随机算法,可以让二叉查找树的期望高度为O(lgn),则前面那些操作的最坏时间就可以为O(lgn)。非常高效的算法。

代码如下:

#include <iostream>
using namespace std;

struct TreeNode
{
	int key;
	TreeNode *p;
	TreeNode *left;
	TreeNode *right;
};

//中序遍历,实现从小到大输出序列
void InorderTreeWalk(TreeNode *root)
{
	if(root!=NULL)
	{
		InorderTreeWalk(root->left);
		cout<<root->key<<" ";
		InorderTreeWalk(root->right);
	}
}

//查询二叉查找树,在数中查找k,返回其指针(递归版)
TreeNode* TreeSearch(TreeNode *root,int k)
{
	if(root!=NULL)
	{
		if(root->key==k)
		{
			return root;
		}
		else if(key<root->key)
		{
			TreeSearch(root->left,k);
		}
		else if(key>root->key)
		{
			TreeSearch(root->right,k);
		}
	}
	else
	{
		return NULL;
	}
}


//查询二叉查找树,在数中查找k,返回其指针(迭代版)
TreeNode* TreeSearch(TreeNode *root,int k)
{
	while(root!=NULL&&root->key!=k)
	{
		if(root->key>k)
		{
			root=root->left;
		}
		else if(root->key<k)
		{
			root=root->right;
		}
	}
	return root;
}

//返回指向最小值的指针
TreeNode* TreeMinimum(TreeNode *root)
{
	if(root->left!=NULL)
	{
		TreeMinimum(TreeNode->left);
	}
	return root;
}

//返回指向最大值的指针
TreeNode* TreeMaxmum(TreeNode *root)
{
	if(root->right!=NULL)
	{
		TreeMaxmum(root->right);
	}
	return root;
}

//返回x的后继的指针
TreeNode* TreeSuccessor(TreeNode *x)
{
	if(x->right!=NULL)
	{
		return TreeMinimum(x->right);
	}
	
	//如果x没有右儿子,那么它的后继肯定是它的一个祖先。而且它肯定在它这个祖先的左枝上。而且这个祖先是符合条件的祖先中离他最近的那一个。
	TreeNode *y=x->p;
	while(y!=NULL && x==y->right)
	{
		x=y;
		y=y->p;
	}
	return y;
}

//返回x的前驱的指针
TreeNode* TreePredecessor(TreeNode *x)
{
	if(x->left!=NULL)
	{
		return TreeMaxmux(x->left);
	}
	
	//如果x没有左儿子,那么它的前驱肯定是他的一个祖先。而且它肯定在它这个祖先的右枝上。而且这个祖先是符合条件的祖先中离它最近的那一个。
	y=x->p;
	while(y!=NULL && x==y->right)
	{
		x=y;
		y=y->p;
	}
	return y;
}
//插入操作:把一个新值v插入到二叉查找树中。(递归版)
void TreeInsert(TreeNode *root,int v,TreeNode *y)//让y指向v的父节点
{
	if(root==NULL)
	{
		root=new TreeNode;
		if(root==NULL)
		{
			return;
		}
		root->key=v;
		root->p=y;
		root->left=NULL;
		root->right=NULL;
		if(y!=NULL)
		{
			if(y->key<v)
			{
				y->right=root;
			}
			else
			{
				y->left=root;
			}
		}
	}
	else
	{
		y=root;
		if(root->key > v)
		{
			TreeInsert(root->left,v,y);
		}
		else
		{
			TreeInsert(root->right,v,y);
		}
	}
}

//插入操作:把一个新值v插入到二叉查找树中。( 迭代版)
void TreeInsert(TreeNode *root,int v)
{
	TreeNode *y=root;
	while(root!=NULL)
	{
		y=root;
		if(root->key > k)
		{
			root=root->left;
		}
		else
		{
			root=root->right;
		}
	}
	root=new TreeNode;
	if(root==NULL)
	{
		return;
	}
	root->key=k;
	root->p=y;
	root->left=NULL;
	root->right=NULL;
	if(y!=NULL)
	{
		if(y->key<v)
		{
			y->right=root;
		}
		else
		{
			y->left=root;
		}
	}
}

//删除某个值v所在的节点
void TreeDelete(TreeNode *root,int v)
{
	//先找到v所在的节点
	TreeNode *z=TreeSearch(root,v);
	if(z==NULL)
	{
		return;
	}
	
	TreeNode *y=NULL;//令y指向被删除节点。则y要么是z(z只有一个子女或无子女),要么是z的后继节点(z有两个子女)。
	if(z->left==NULL || z->right==NULL)
	{
		y=z;
	}
	else
	{
		y=TreeSuccessor(z);
		if(y==NULL)//z无后继结点
		{
			y=z;
		}
	}
	
	TreeNode *x=NULL;//令x指向y的非空子女,如果y无子女则x为NULL
	if(y->left!=NULL)
	{
		x=y->left;
	}
	else
	{
		x=y->right;
	}
	
	//修改y和x相关的指针
	if(x!=NULL)
	{//若x不为空,修改x的父节点为y的父节点
		x->p=y->p;
	}
	if(y->p!=NULL)
	{//如果y的父节点不为空,则修改y的父节点的子女,将x放到y所在的位置
		if(y==(y->p)->left)
		{
			(y->p)->left=x;
		}
		else
		{
			(y->p)->left=x;
		}
	}
	else
	{//如果y的父节点为空,则y为树根。此时应将x置为树根。
		root=x;
	}
	
	if(y!=z)//如果要删除的是z的后继,则将后继结点数据复制到z
	{
		z->key=y->key;
	}
	
	delete y;
}
//随机化算法:本地操作,a0与(a0,...,an-1)交换,。。。。。
void Random(int *a,int n)
{
	srand((unsigned int)time(NULL));
	for(int i=0;i<n;i++)
	{
		int j=rand()%(n-i)+i;
		int temp=a[i];
		a[i]=a[j];
		a[j]=temp;
	}
}

//随机构造二叉查找树(之所以引入随机化,是为了让二叉查找树的期望高度为O(lgn)
void RandomCreateTree(TreeNode *root,int *a,int n)
{
	//先用随机化算法将数组进行随机化
	Random(a,n);
	root=NULL;
	for(int i=0;i<n;i++)
	{
		TreeInsert(root,a[i]);
	}
}
void Output(int *a,int n)
{
     for(int i=0;i<n;i++)
     {
         printf("%d ",a[i]);
     }
     cout<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值