二叉查找树(BST)
定义:
又称二叉排序树,二叉搜索树;具备以下特性的树称为二叉查找树:
1.左子树上所有结点的值均小于它的根结点的值;
2.右子树上所有结点的值均大于它的根结点的值;
3.左、右子树也分别为二叉查找树。
typedef struct STreeNode* pSTreeNode;
typedef int TreeKeyType;
struct STreeNode
{
TreeKeyType key;//节点数据
//节点指针
pSTreeNode pLeftChild;//指向左子树
pSTreeNode pRightChild;//指向右子树
STreeNode( TreeKeyType Value )//构造函数
{
key = Value;
pLeftChild = NULL;
pRightChild = NULL;
}
};
查找:
1、若待查找的元素值和根节点相同,则返回根节点;
2、若待查找的元素值小于根节点的元素值,则递归从根节点的左子树中查找;
3、若待查找的元素值大于根节点的元素值,则递归从根节点的右子树中查找。
//查找成功,返回指向该节点的指针
//查找失败,返回NULL
//pNode-二叉查找树;Value-查找值
pSTreeNode Search( pSTreeNode pNode, TreeKeyType Value )
{
//没有找到
if ( pNode == NULL )
return NULL;
//待查找的元素值与根节点的值相同
if ( pNode->key == Value )
return pNode;
else
{
//带查找的元素值小于根节点的元素值
if ( pNode->key > Value )
//在左子树中递归查找
return Search( pNode->pLeftChild, Value );
//带查找的元素值大于根节点的元素值
else
//在右子树中递归查找
return Search( pNode->pRightChild, Value );
}
}
插入:
1、若当前二叉树为空,则插入的元素为根节点;
2、若插入的元素值小于根节点值,则递归从根节点的左子树中找到可插入位置;
3、若插入的元素值大于根节点值,则递归从根节点的右子树中找到可插入位置。
//插入成功返回true;插入失败返回false //pNode-二叉查找树;Value-插入值 bool Insert( pSTreeNode pNode, TreeKeyType Value ) { if ( pNode == NULL) pNode = new STreeNode( Value ); //插入元素的值小于根节点的值 if ( pNode->key > Value ) //插入到左子树 Insert( pNode->pLeftChild, Value ); //插入节点的值大于根节点的值 else if( pNode->key < Value ) //插入到右子树 Insert( pNode->pRightChild, Value ); //插入节点的值等于根节点的值 else //树中已有关键字相同的节点,不再插入 return false; return true; }
删除:
1、待删除节点Z为叶子节点,则直接删除该节点。修改父节点的指针;
2、待删除节点Z为单支节点(只有左子树或者右子树),让Z的子树与Z的父节点相连,删除节点Z;
3、待删除节点Z左右子树都不为空。
方法一:找到Z的后继节点Y,Y一定没有左子树,用Y替换Z,并让Y的父亲节点成为Y的右子树的父亲节点,接着删除Y。
方法二:找到Z的前驱节点X,X一定没有右子树,用X替换Z,并让X的父亲节点成为X的左子树的父亲节点,接着删除X。
以方法二为例:
void CBinTree::Delete( pSTreeNode pNode,TreeKeyType Value )
{
pSTreeNode pParentNode = pNode;
pSTreeNode pFindNode = pNode;
//循环结束后,pFindNode指向待删除节点
//pParentNode指向待删除结点的父节点
while ( pFindNode != NULL )
{
if ( pFindNode->key == Value )
break;
pParentNode = pFindNode;
if ( pFindNode->key > Value )
pFindNode = pFindNode->pLeftChild;
else
pFindNode = pFindNode->pRightChild;
}
//没有找到该节点,不需要做删除操作
if ( pFindNode == NULL )
return;
// 该节点只有一棵子树或者为叶子节点
if ( pFindNode->pLeftChild == NULL || pFindNode->pRightChild == NULL )
{
// 该节点为叶子节点
pSTreeNode pTemp = NULL;
//该节点只有左子树
if ( pFindNode->pLeftChild != NULL)
pTemp = pFindNode->pLeftChild;
//该节点只有右子树
else if ( pFindNode->pRightChild != NULL )
pTemp = pFindNode->pRightChild;
//该节点为其父节点的左孩子
if ( pParentNode->pLeftChild == pFindNode )
//将该节点的子树变为其父节点的左子树
pParentNode->pLeftChild = pTemp;
//该节点为其父节点的有孩子
else
//将该节点的子树变为其父节点的右子树
pParentNode->pRightChild = pTemp;
delete pFindNode;
pFindNode = NULL;
}
//该节点的左右子树均不为空
else
{
pSTreeNode pTemp = pFindNode->pLeftChild;
pSTreeNode pTempParent = pFindNode;
//循环找到该节点左子树的右侧尽头的节点
//即待删除节点的前驱节点pTemp(中序遍历的前驱)
//该节点的前驱节点为左子树中关键字值最大的节点
while ( pTemp->pRightChild != NULL )
{
pTempParent = pTemp;//前驱结点的父节点
pTemp = pTemp->pRightChild; //一直往右下方走
}
pFindNode->key = pTemp->key;//pTemp替换pFindNode
pTempParent->pRightChild = pTemp->pLeftChild;
delete pTemp;
pTemp = NULL;
}
}
遍历:
1、前序遍历(递归):先根节点,后左孩子节点,再右孩子结点。
2、中序遍历(递归):先左孩子结点,后根节点,再右孩子结点。
3、后序遍历(递归):先做孩子节点,后右孩子结点,再根节点。
//前序遍历
void CBinTree::PreorderRecursively( pSTreeNode pNode )
{
if (pNode == NULL)
return;
cout << " " << pNode->key << " ";
PreorderRecursively( pNode->pLeftChild );//左子树
PreorderRecursively( pNode->pRightChild );//右子树
}
//中序遍历
void CBinTree::InorderRecursively( pSTreeNode pNode )
{
if (pNode == NULL)
return;
InorderRecursively( pNode->pLeftChild );
cout << " " << pNode->key << " ";
InorderRecursively( pNode->pRightChild );
}
//后序遍历
void CBinTree::PostorderRecursively( pSTreeNode pNode )
{
if (pNode == NULL)
return;
PostorderRecursively( pNode->pLeftChild );
PostorderRecursively( pNode->pRightChild );
cout << " " << pNode->key << " ";
}
原文链接 https://blog.csdn.net/lining0420/article/details/76167901