二叉搜索树探究
完整代码:https://github.com/zzaiyuyu/BinarySearchTree
插入结点
根据二叉搜索树的定义,左孩子<根节点值<右孩子。寻找需要插入的位置,不难发现最后的位置一定是NULL,而不会存在于两个结点中间。另外用parent指针记录待插位置的父节点。
非递归:
void InsertBSTree(BSTNode** pRoot, DataType data)
{
assert(pRoot);
if (NULL == *pRoot) {
//空树,改变头结点
*pRoot = BuyNode(data);
}
else {
//树不空,找到合适位置插入
BSTNode *parent = NULL;
BSTNode *child = *pRoot;
//最后插入的位置一定是child为空,而不牵扯在两数中间插入
while (child) {
if (child->_data > data) {
parent = child;
child = child->_pLeft;
}
else {
parent = child;
child = child->_pRight;
}
}
//找到了child的合适位置
if (parent->_data > data) {
parent->_pLeft = BuyNode(data);
}
else {
parent->_pRight = BuyNode(data);
}
}
}
递归:
int InsertBSTreeR(BSTNode** pRoot, DataType data)
{
if (NULL == *pRoot) {
*pRoot = BuyNode(data);
return 1;
}
else if ((*pRoot)->_data > data) {
return InsertBSTreeR(&(*pRoot)->_pLeft, data);
}
else if ((*pRoot)->_data < data) {
return InsertBSTreeR(&(*pRoot)->_pRight, data);
}
return 0;
}
查找结点
非递归:
BSTNode* FindBSTree(BSTNode* pRoot, DataType data)
{
BSTNode *pCur = pRoot;
while (pCur) {
if (pCur->_data == data) {
break;
}
else if (pCur->_data > data) {
pCur = pCur->_pLeft;
}
else {
pCur = pCur->_pRight;
}
}
return pCur;
}
递归:
BSTNode* FindBSTreeR(BSTNode* pRoot, DataType data)
{
if (pRoot) {
if (data == pRoot->_data) {
return pRoot;
}
else if (pRoot->_data > data) {
return FindBSTreeR(pRoot->_pLeft, data);
}
else {
return FindBSTreeR(pRoot->_pRight, data);
}
}
return NULL;
}
删除结点
删除结点需要考虑的情况较多。
首先找到待删除结点的位置,然后分三种情况进行删除。
- 右子树为空,叶结点和非叶处理方法是一样的,而要单独考虑是不是根节点。根节点也就是没有父节点的情况。
- 左子树为空,同上。
- 左右均不空,在右子树中找到最左结点(右子树最小的),和待删结点交换值,删除这个最小结点。需要分情况讨论右子树有一个或多个结点的情况。
递归和非递归解法相似,只是寻找待删结点时可以替换。
扫描二维码关注公众号,回复:
1434464 查看本文章
void DeleteBSTree(BSTNode** pRoot, DataType data)
{
assert(pRoot);
if (*pRoot) {
//找到要删除的结点
BSTNode *parent = NULL;
BSTNode *pDel = *pRoot;
while (pDel) {
if (pDel->_data == data) {
break;
}
else if (pDel->_data > data) {
parent = pDel;
pDel = pDel->_pLeft;
}
else {
parent = pDel;
pDel = pDel->_pRight;
}
}
//分情况删除,右子树为空,左子树为空,左右均存在
if (NULL == pDel) {
return;
}
if (NULL == pDel->_pLeft ) {
if (parent) {
if (parent->_data > pDel->_data) {
parent->_pLeft = pDel->_pRight;
}
else {
parent->_pRight = pDel->_pRight;
}
}
else {
//删根节点
*pRoot = pDel->_pRight;
}
}
else if (pDel->_pRight == NULL) {
if (parent) {
if (parent->_data > pDel->_data) {
parent->_pLeft = pDel->_pLeft;
}
else {
parent->_pRight = pDel->_pLeft;
}
}
else {
//删根节点
*pRoot = pDel->_pLeft;
}
}
else {
//在右子树中找最小的结点
BSTNode *pOrg = pDel; //记录被删除的结点
parent = pDel;
pDel = pDel->_pRight;
while (pDel->_pLeft) {
parent = pDel;
pDel = pDel->_pLeft;
}
//交换被删除结点和真正被删除的结点值
pOrg->_data = pDel->_data;
//删除找到的最小节点
if (parent == pOrg) {
parent->_pRight = pDel->_pRight;
}
else {
parent->_pLeft = pDel->_pRight;
}
}
free(pDel);
}
}
性能
最差情况下,例如以 1为根节点的序列 1 2 3 4 5 ,形成单支树,查找的时间复杂度变为O(n)。最好的情况形成完全二叉树,时间复杂度为O(log n)。
如何解决搜索树退化为单支树失去性能优势的问题?
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度。
引入AVL树和红黑树。