二叉查找树


前端时间又看了看二叉查找树,本来早该写出程序的,只是这几天心情差极了,前天晚上大醉,痛苦的折磨了一晚。现在的心情还是一样不怎么好,一直没有心情好好看看,胸口憋闷,我又一次陷入如此的轮回。I NEED FRESH AIR!

不啰嗦了,不管怎么样,还是抛弃一切学习吧,只有这样才会朝着自己的梦想更进一步。

二叉查找树,又名二叉搜索树。它是这样一棵树,根节点,若有左孩子,那么左孩子的关键值比它的小;若右子树不空,那么右孩子的关键值比它的大。它的左右孩子分别也是一棵二叉查找树。二叉搜索树的一个最重要的应用我想应该就是利用二叉树进行排序吧。一棵二叉搜索树,它的中序遍历的结果就是一个升序的有序序列了。当然还有,差点忘记了,它的名字,当然最重要还有查找一个节点的效率相对来说比较高,查找的时间复杂度应该是logn吧,因为每一次查找时就会走一棵分叉的树,这样就最多去掉了另一半的节点不用管了。

我的代码有建立一个二叉搜索树,查找,插入,删除等基本操作。建立一个二叉搜索树,其实也就相当于从一个空节点开始插入要保存的值。我在这里,是输入了一个数组,将数组中的值保存到二叉树中。对于查找,可以用递归来实现,也可以用迭代。查找的过程,就是从跟节点开始,如果与要查找的值相等就返回,若比跟节点的值小,那么就递归查找左子树,否则递归查找右子树。插入操作,也不算难,找到相应的位置就好了,还是像查找一样,比较关键值的大小,直到找到它相应的位置,插入节点,并修改相应节点的指针。最复杂是操作应该就是删除了,重点多说两句删除吧,因为我自己在写代码时总会出现一些错误。

删除,要分三种情况。

(1)当要删除的节点左右孩子都为空,即此节点为叶节点时,这时删除此节点并不会影响后续的树的结构,所以只需要修改父节点相应的指针就可以了。注意,此时要分此节点是否为根节点。

(2)要删除的节点为单支节点。即,要么只有左孩子,要么只有右孩子。然后根据此节点是父节点的左孩子还是右孩子来区分,如是父节点的左孩子,那么将当前节点的孩子节点(可能是左孩子或右孩子)赋给左孩子,反之赋给右孩子。然后删除此节点。

(3)要删除的节点的左右孩子都不空。在这种情况下,相当于一个三角形,要删除最上面的顶角,但是不能修改整体的数据结构,那么就需要找到一个相应的数来代替当前这个顶角。因为左孩子比顶角小,右孩子比顶角大,所以要找到一个数填补到这里也要满足此性质。那么我们就会想,沿着顶角的左孩子的右分支走,一直走到最后,那么那个值肯定左孩子大,而又小于右孩子。所以在这种情况下,就是要遍历当前节点的左孩子的右孩子,然后将那个最后的右孩子的值赋给当前节点,而且要修改最后的右孩子节点的父节点的指针,最后删除这个右孩子节点。此时就会满足二叉查找树的性质。

在删除节点时,因为删除这个节点会造成其父节点指针的断接,所以在查找要删除的节点时,一定要记录下此节点的父节点,以便删除时来修改指针值。

下面是代码:

struct Tree
{
	int data;
	Tree *lchild;
	Tree *rchild;
};

Tree *CreateBinarySearchTree(int arr[],int len)
{
	Tree *root = new Tree;
	Tree *p = root;
	root->data = arr[0];
	root->lchild = NULL;
	root->rchild = NULL;
	for(int i = 0;i < len; ++i)
	{
		Tree *tmp = new Tree;
		tmp->data = arr[i];
		tmp->lchild = NULL;
		tmp->rchild = NULL;

		root = p;
		if(tmp->data == root->data)
			continue;
		while(root)
		{
			if(tmp->data < root->data && root->lchild == NULL)
			{
				root->lchild = tmp;
				break;
			}
			else if(tmp->data < root->data)
			{
				root = root->lchild;
			}
			else if(tmp->data > root->data && root->rchild == NULL)
			{
				root->rchild = tmp;
				break;
			}
			else
			{
				root = root->rchild;
			}
		}
	}

	return p;
}

为了查看自己建立的这个二叉搜索树是否正确,可以中序遍历一下这颗树,看一看输出结果是否有序。

void InOrderTravel(Tree *root)
{
	if(root == NULL)
		return;
	InOrderTravel(root->lchild);
	printf("%d ",root->data);
	InOrderTravel(root->rchild);
}

下面是插入操作的代码:

void InsertNode(Tree *root,int num)
{
	if(num == root->data)
		return;
	Tree *tmp = new Tree;
	tmp->data = num;
	tmp->lchild = NULL;
	tmp->rchild = NULL;
	Tree *p = root;
	while(p)
	{
		if(num < p->data && p->lchild == NULL)
		{
			p->lchild = tmp;
			break;
		}
		else if(num < p->data)
		{
			p = p->lchild;
		}
		else if(num > p->data && p->rchild == NULL)
		{ 
			p->rchild = tmp;
			break;
		}
		else
		{
			p = p->rchild;
		}
	}

}

下面是查找操作的代码:

bool FindNode(Tree *root,int num,Tree *p)
{
	if(root == NULL)
	{
		p = NULL;
		return false;
	}
	if(num == root->data)
	{
		p->data = root->data;
		p->lchild = root->lchild;
		p->rchild = root->rchild;
		return true;
	}
	else if(num > root->data)
	{
		return FindNode(root->rchild,num,p);
	}
	else
	{
		return FindNode(root->lchild,num,p);
	}
}

下面是最麻烦的删除操作的代码:

bool delete_node(Tree *root,int num)
{
	Tree *t = root;
	Tree *q;
	bool find = false;

	while(t && !find)
	{
		if(num == t->data)
		{
			find = true;
		}
		else if(num > t->data)
		{
			q = t;
			t = t->rchild;
		}
		else
		{
			q = t;
			t= t->lchild;
		}
	}
	if(t == NULL)
		cout << "没有找到要删除的节点!" << endl;

	// t为要删除的节点,q为t的父亲节点
	if(t->lchild == NULL && t->rchild == NULL) // t为叶节点
	{
		if(t == root)
		{
			root = NULL;
		}
		else
		{
			if(q->lchild == t)
			{
				q->lchild = NULL;
			}
			else
			{
				q->rchild = NULL;
			}
		}
		delete t;
	}
	else if(t->lchild == NULL && t->rchild != NULL) // t左子树空
	{
		if(t == root)
		{
			root = t->rchild;
		}
		else
		{
			if(q->lchild == t)
			{
				q->lchild = t->rchild;
			}
			else
			{
				q->rchild = t->rchild;
			}
		}
		delete t;
	}
	else if(t->lchild != NULL && t->rchild == NULL) // t右子树空
	{
		if(t == root)
		{
			root = t->lchild;
		}
		else
		{
			if(q->lchild == t)
			{
				q->lchild = t->lchild;
			}
			else
			{
				q->rchild = t->lchild;
			}
		}
		delete t;
	}
	else // t左右子树都不空
	{
		
			Tree *p = t->lchild;
			Tree *s = p;
			while(p->rchild)
			{
				s = p;
				p = p->rchild;
			}
			t->data = p->data;
			if(p == s)
			{
				t->lchild = p->lchild;
			}
			else
			{
				s->rchild = p->lchild;
			}

			delete p;
		
	}

	return find;
}

下面是我一段测试代码:

void main()
{
	int data[] = {6,4,5,2,1,3,9,8};
	Tree *t = CreateBinarySearchTree(data,8);
	cout << "原始序列:";
	InOrderTravel(t);
	cout << endl;
	InsertNode(t,7);
	cout << "插入节点7后:";
	InOrderTravel(t);
	cout << endl;
	InsertNode(t,0);
	cout << "插入节点0后:";
	InOrderTravel(t);
	cout << endl;
	InsertNode(t,-4);
	cout << "插入节点-4后:";
	InOrderTravel(t);
	cout << endl;
	Tree *find = new Tree;
	if(FindNode(t,8,find))
	{
		cout << "查找到数:" << find->data << endl;
	}
	delete_node(t,4);
	cout << "删除节点4后:";
	InOrderTravel(t);
	cout << endl;

}
输出结果如下:

Ok,That's all!如果大家发现什么错误或者需要改进的地方,请留言指正,大家共同学习吧。

I need fresh air!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值