二叉搜索树的实现
最近复习了二叉搜索树的基础知识,总结下,然后用C++实现二叉搜索树的插入,删除,查找等,也是为了实现红黑树做铺垫。
一个二叉搜索树结构如下,父节点做子树都比父节点小,右子树都比父节点大。
插入节点12后,如下
删除的情况,删除节点A,判断节点A是否为叶子节点,如果是叶子结点直接删除即可。如果叶子A有且仅有一个子节点B,那么用B替代节点A。
如果节点A有两个子节点,找到前驱节点B,用前驱节点B(或者后继节点)替代节点A。有一种特殊的情况,就是前驱节点有左孩子,或者后继节点有右孩子,
这种情况需要仔细考虑。仅拿前驱节点举例子,前驱节点的父节点为C,前驱节点的左孩子为D,那么将D的父节点间设置为C,C的子节点设置为D。如果前驱节点B有右孩子怎么办?B是不可能有右孩子的,否则他的右孩子就是节点A的前驱节点。因为对于一个双子树的节点,他的前驱节点必然为左子树最大节点。
在这里再叙述一下如何查看一个节点的前驱节点和后继几点:
`前驱节点`:
`1 如果节点A有左子树,那么他的前驱节点为左子树中最大的节点。`
`2 如果节点A没有左子树,需要考察节点A的父节点,如果节点A是其父节点的右孩子,那么父节点为前驱节点,否则将父节点设置为当前节点A,继续向上查找,直到找到某个节点为其父节点的右孩子,这个父节点就是要找的前驱节点。`
`后继节点`:
`1 如果节点A有右子树,那么他的后继节点为其右子树的最小节点。`
`2 如果节点A没有右子树,那么同样遍历其父节点,找到某个节点是其父节点的左孩子,那么这个父节点就是要找的后继节点。`
如图
节点10的后继节点为12,为其右子树中最小的节点。
节点10的前驱节点为6,为其左子树中最大的节点。
节点12的前驱节点为10,因为节点12没有左子树,父节点为13,12不是13的右节点,需要继续向上查找,找到节点10,13是10的右节点,节点10为12的前驱节点。
节点6的后继节点为10,同样是向父节点找,直到找到某个节点是其父节点的左子树,5是10的左子树,所以10为6的后继节点。
节点5的前驱节点为NULL,因为节点5没有左子树,所以向上查找,直到找到根节点也不满足右节点的规则,所以节点5的前驱节点为NULL
删除节点10,找到前驱节点6替换10,如果节点6有左子树,那么将左子树挂接到节点5的右节点。
如下图:
下面用代码实现上述插入,删除以及中序遍历的逻辑。
先实现树的节点
class TreeNode { public: TreeNode():m_pParent(NULL), m_pLeftChild(NULL), m_pRightChild(NULL), m_nData(0){} TreeNode(const TreeNode & tree){ this->m_nData = tree.m_nData; this->m_pParent = NULL; this->m_pLeftChild = NULL; this->m_pRightChild = NULL; } TreeNode(int data):m_nData(data), m_pRightChild(NULL), m_pLeftChild(NULL), m_pParent(NULL){} ~TreeNode(){ this->m_nData = 0; this->m_pLeftChild = NULL; this->m_pParent = NULL; this->m_pRightChild = NULL; } TreeNode& operator =(const TreeNode& treeNode)//赋值运算符 { if (this != &treeNode) { this->m_pParent = treeNode.m_pParent; this->m_pLeftChild = treeNode.m_pLeftChild; this->m_pRightChild = treeNode.m_pRightChild; this->m_nData = treeNode.m_nData; } return *this; } friend ostream & operator<<(ostream &out, TreeNode &obj) { out << " " << obj.m_nData <<" "; return out; } TreeNode * m_pParent; TreeNode * m_pLeftChild; TreeNode * m_pRightChild; int m_nData; };
实现树类
class TreeClass { public: TreeClass():m_pRoot(NULL){} ~TreeClass(){ if(!m_pRoot) { return; } //找到树的最小节点 TreeNode * treeNodeTemp = findMinNode(m_pRoot); vector<TreeNode *> treenodeVec; while(treeNodeTemp) { treenodeVec.push_back(treeNodeTemp); treeNodeTemp = findNextNode(treeNodeTemp); } for(int i = 0; i < treenodeVec.size(); i++) { delete(treenodeVec[i]); } treenodeVec.clear(); m_pRoot = NULL; } void initial( list<int>& data); void visitTree(); //搜索前继节点 TreeNode *findPreNode(TreeNode * treeNode); //搜索后继节点 TreeNode * findNextNode(TreeNode * treeNode); //寻找一个子树最小节点 TreeNode * findMinNode(TreeNode * root); //寻找一个子树最大节点 TreeNode * findMaxNode(TreeNode * root); //删除一个节点 void deleteTreeNode(TreeNode* treeNode); //删除的节点没有子节点 void deleteNoChildNode(TreeNode * treeNode); //删除的节点有一个子节点 void deleteOneChildNode(TreeNode * treeNode, TreeNode * childNode); //删除节点有两个孩子,找到后继节点或者前驱节点替换即可,这里选择前驱节点 void deleteTwoChildNode(TreeNode * treeNode); //用前驱节点替换当前节点 void preupdateNode(TreeNode * preNode, TreeNode * treeNode); void eraseNode(int i); TreeNode * findTreeNode(int i); TreeNode * m_pRoot; };
有几个函数需要着重说明一下:
初始化函数,通过列表初始化为一棵树
void TreeClass::initial( list<int>& data) { for( list<int>::iterator listIter = data.begin(); listIter != data.end(); listIter++) { TreeNode * treeNode = m_pRoot; if(!treeNode) { m_pRoot = new TreeNode(*listIter); continue; } TreeNode * nodeParent = NULL; bool findflag = true; while(treeNode) { if(treeNode->m_nData < *listIter) { nodeParent = treeNode; treeNode = treeNode->m_pRightChild; } else if(treeNode->m_nData > *listIter) { nodeParent = treeNode; treeNode = treeNode->m_pLeftChild; } else { //找到树中有相等的节点,那么不插入,也可以完善为次数+1 findflag = false; break; } } if(findflag) { if(nodeParent->m_nData <*listIter ) { TreeNode * treeNodeTemp =new TreeNode(*listIter); nodeParent->m_pRightChild = treeNodeTemp; treeNodeTemp->m_pParent = nodeParent; } else { TreeNode * treeNodeTemp =new TreeNode(*listIter); nodeParent->m_pLeftChild = treeNodeTemp; treeNodeTemp->m_pParent = nodeParent; } } } }
中序遍历
//中序遍历 void TreeClass::visitTree() { if(!m_pRoot) { cout << "empty tree!!!"; } //找到树的最小节点 TreeNode * treeNodeTemp = findMinNode(m_pRoot); while(treeNodeTemp) { cout << (*treeNodeTemp); treeNodeTemp = findNextNode(treeNodeTemp); } }
查找一个子树中最小节点
//寻找一个子树最小节点 TreeNode * TreeClass::findMinNode(TreeNode * root) { if(!root) { return NULL; } TreeNode * treeNode = root; while(treeNode->m_pLeftChild) { treeNode = treeNode->m_pLeftChild; } return treeNode; }
查找一个子树中最大节点
TreeNode * TreeClass::findMaxNode(TreeNode * root) { if(!root) { return NULL; } TreeNode * treeNode = root; while(treeNode->m_pRightChild) { treeNode = treeNode->m_pRightChild; } return treeNode; }
查找一个节点前驱节点
//搜索前驱节点 TreeNode* TreeClass::findPreNode(TreeNode * treeNode) { //左子树不为空,找到左子树中最大节点 if( treeNode->m_pLeftChild ) { TreeNode * treeNodeTemp = findMaxNode( treeNode->m_pLeftChild); return treeNodeTemp; } //左子树为空 //找到父节点,如果该节点是父节点的右孩子,那么父节点为前驱节点。 //如果不满足,则将父节点设为该节点,继续向上找,知道满足条件或者父节点为空 TreeNode * treeNodeTemp = treeNode->m_pParent; while(treeNodeTemp && treeNode != treeNodeTemp->m_pRightChild) { treeNode = treeNodeTemp; treeNodeTemp = treeNodeTemp ->m_pParent; } return treeNodeTemp; }
查找一个节点的后继节点
//搜索后继节点 TreeNode * TreeClass::findNextNode(TreeNode * treeNode) { //右子树不为空,找到右子树中最小节点 if( treeNode->m_pRightChild ) { TreeNode * treeNodeTemp = findMinNode( treeNode->m_pRightChild); return treeNodeTemp; } //右子树为空 //找到父节点,如果该节点是父节点的左孩子,那么父节点为后继节点。 //如果不满足,则将父节点设为该节点,继续向上找,直到满足条件或者父节点为空 TreeNode * treeNodeTemp = treeNode->m_pParent; while(treeNodeTemp && treeNode != treeNodeTemp->m_pLeftChild) { treeNode = treeNodeTemp; treeNodeTemp = treeNodeTemp ->m_pParent; } return treeNodeTemp; }
根据数据查找某个节点
TreeNode * TreeClass::findTreeNode(int i) { TreeNode *treeNode = m_pRoot; while(treeNode) { if(treeNode->m_nData > i) { treeNode = treeNode->m_pLeftChild; } else if(treeNode->m_nData < i) { treeNode = treeNode->m_pRightChild; } else { return treeNode; } } return treeNode; }
某个节点有两棵子树,删除这个节点,函数如下:
void TreeClass::deleteTwoChildNode(TreeNode * treeNode) { //找到前驱节点 TreeNode* preNode = findPreNode(treeNode); preupdateNode(preNode, treeNode); delete(treeNode); }
详细的删除和替换细节:
void TreeClass::preupdateNode(TreeNode * preNode, TreeNode * treeNode) { preNode->m_pRightChild = treeNode->m_pRightChild; treeNode->m_pRightChild->m_pParent = preNode; //判断前驱节点是否为该节点的左孩子 if(treeNode->m_pLeftChild != preNode) { TreeNode * prechild = NULL; if(preNode->m_pLeftChild) { prechild = preNode->m_pLeftChild; } preNode->m_pLeftChild = treeNode->m_pLeftChild; treeNode->m_pLeftChild->m_pParent = preNode; if(preNode->m_pParent->m_pLeftChild == preNode) { preNode->m_pParent->m_pLeftChild =prechild; if(prechild) { prechild->m_pParent = preNode->m_pParent; } } else { preNode->m_pParent->m_pRightChild =prechild; if(prechild) { prechild->m_pParent = preNode->m_pParent; } } } if(treeNode->m_pParent == NULL) { preNode->m_pParent = NULL; m_pRoot = preNode; return; } //节点有父节点,需要将父节点和前驱节点绑定 preNode->m_pParent = treeNode->m_pParent; if(treeNode->m_pParent->m_pLeftChild == treeNode) { treeNode->m_pParent->m_pLeftChild = preNode; } else { treeNode->m_pParent->m_pRightChild = preNode; } }
如果节点没有子树,那么直接删除,如果节点有一颗子树,那么用该子树替代这个节点即可。
代码下载地址:
https://github.com/secondtonone1/searchtree
测试代码:
int array[6]={7,4,2,5,3,8}; list<int> mylist; mylist.insert(mylist.end() , array ,array+6); TreeClass treeClass; treeClass.initial(mylist); treeClass.visitTree(); cout << endl; treeClass.eraseNode(7); treeClass.visitTree(); cout << endl; treeClass.eraseNode(4); treeClass.visitTree(); cout << endl; int array2[5]={7,4,2,5,8}; list<int> mylist2; mylist2.insert(mylist2.end() , array2 ,array2+5); TreeClass treeClass2; treeClass2.initial(mylist2); treeClass2.visitTree(); cout << endl; treeClass2.eraseNode(4); treeClass2.visitTree(); cout << endl; int array3[6]={7,4,2,6,5,8}; list<int> mylist3; mylist3.insert(mylist3.end() , array3 ,array3+6); TreeClass treeClass3; treeClass3.initial(mylist3); treeClass3.visitTree(); cout << endl; treeClass3.eraseNode(7); treeClass3.visitTree(); cout << endl; getchar(); return 0;
打印输出:
我的公众号谢谢关注: