基本数据结构——二叉查找树

二叉查找树又称为二叉排序树,他要么是一颗空树,要么满足以下性质:

(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

(3)左、右子树也分别为二叉查找树;

        值得注意两点:

对于同一个序列,二叉查找树并不唯一,这使得二叉查找树基本操作的最坏情况运行时间在O(logN)到O(N)(当选择最小或最大值作为根节点且构成线性链时)之间,对于随机构造的二叉查找树,其期望高度为O(logN),从而基本操作的平均时间为O(logN)。

        即便是对于随机构造的二叉查找树,经过多次插入,删除后,该树的高度也有可能接近O(N),因此二叉查找树实际应用中性能并不一定会很理想,这就引申出红黑树,简而言之,红黑树即采用了一系列手段保证高度接近O(logN)的一种二叉查找树,红黑树在标准C++的STL中应用广泛。

如果要求找出二叉查找树中序遍历顺序下一个节点的后继,则对于任一节点来说,其后继要么不存在,要么是大于该节点关键值中最小的一个,亦即,如果该节点有右子树的话,找到他的右子树中的最小值即可,如果没有呢?

查询一个节点后继的C++代码:

Node *Min(Node *root)
{
	while(root->leftChild!=NULL)
		root=root->leftChild;
	return root;
}

//查找节点node的后继
Node *Successor(Node *node)
{
	//右子树不为空的情况,直接返回右子树的最小值
	if(node->rightChild!=NULL)
	{
		return Min(node->rightChild);
	}
	//右子树为空
	Node *parent=node->parent;
	while(parent!=NULL&&node==parent->rightChild)
	{
		node=parent;
		parent=parent->parent;
	}
	return parent;
}

二叉查找树的构造即节点的插入过程.

        二叉查找树中,新插入的节点一定是叶子节点,同样的一组元素,不同的插入顺序将会生成不同的二叉查找树,最坏情况是当输入完全有序时,二叉查找树变成单链。

代码如下:

//二叉排序树的插入
void Insert(Node *root,Node *newNode)
{
	Node *parent=0;
	Node *tempNode=root;
	while(tempNode!=NULL)
	{
		if(newNode->data<tempNode->data)
		{
			tempNode=tempNode->leftChild;
		}
		else
		{
			tempNode=tempNode->rightChild;
		}
		parent=tempNode->parent;
	}
	if(parent==NULL)
	{
		root->data=newNode->data;
	}
	else
	{
		if(newNode->data<parent->data)
		{
			parent->leftChild=newNode;
		}
		else
		{
			parent->rightChild=newNode;
		}
		newNode->parent=parent;
	}
}

二叉查找树的删除过程则略显复杂一些,假如要删除节点z,一共有三种情况:

1、z无子女,则直接将z父节点对应的指针域置为NULL即可。

2、z有一个子女,这个也比较简单,用z的子女代替z的位置即可。

3、z有两个子女,此时应该找到z的后继,将z的后继中的值赋给z,然后删除z的后继,这里又分为此后继有无右子女两种情况(该后继肯定没有左子女)。

代码如下:

//在根节点*root的二叉查找树中删除node
Node *DeleteNode1(Node **root,Node *node)
{
	Node *delNode=0;
	Node *childNode;
	//delNode为真正要删除的节点
	if(node->leftChild==NULL||node->rightChild==NULL)
	{
		delNode=node;
	}
	else
	{
		delNode=Successor(node);
	}
	//确定delNode的非NULL子女(如果有的话)
	if(delNode->leftChild!=NULL)
	{
		childNode=delNode->leftChild;
	}
	else
	{
		childNode=delNode->rightChild;
	}
	//判断delNode的子女是否为null
	if(childNode!=NULL)
	{
		childNode->parent=delNode->parent;
	}
	//判断delNode是否为根节点
	if(delNode->parent==NULL)
	{
		*root=childNode;
	}
	//简单的二叉树删除后的连接
	if(delNode==delNode->parent->leftChild)
	{
		delNode->parent->leftChild=childNode;
	}
	if(delNode==delNode->parent->rightChild)
	{
		delNode->parent->rightChild=childNode;
	}
	if(delNode!=node)
	{
		node->data=delNode->data;
	}
	return delNode;
}

当然,除此之外,二叉查找树的前中后序遍历与普通二叉树并无二致。

点赞