返回分类:全部文章 >> 基础知识
返回上级:编程基础 - 二叉树 (Binary Tree)
本文将介绍二叉排序树的基础知识,并用C++实现主要方法。
在查看本文之前,需要一些数据结构和程序语言的基础,要对“树”的概念有一些了解。
其中的方法还需要熟悉“栈(stack)”、“队列(queue)”和“递归”。
二叉排序树,也称二叉查找树和二叉搜索树,它可以是空树,或同时满足如下条件:
注意:关键字是否可以重复,需要根据实际需求判断。
之后实例全部使用同一组数据:{ 50, 26, 78, 92, 33, 67, 22, 30, 32 }
它的结构和二叉树完全相同:
// 二叉排序树节点
template<typename T>
class BSTreeNode
{
public:
T element; // 数据
BSTreeNode<T>* leftChild; // 左指针
BSTreeNode<T>* rightChild; // 右指针
BSTreeNode(const T& e)
{
element = e;
leftChild = 0;
rightChild = 0;
}
~BSTreeNode()
{
leftChild = 0;
rightChild = 0;
}
};
查找从根结点开始,逐层开始进行比较,直到找到目标或没有找到目标。且是一个递归的过程。
具体查找过程:
下图展示了,查找36(蓝色),查找67(红色)的过程:
C++代码:
// Author: https://blog.csdn.net/DarkRabbit
// Binary Search Tree
// BSTree查找
// params:
// root: 根结点
// val: 待查找的值
// find: 找到的结点
// findParent: 寻找过程的上一个结点
// return:
// bool: 查找是否成功
template<typename T>
bool BSTreeFind(BSTreeNode<T>* root, const T& val, BSTreeNode<T>*& find, BSTreeNode<T>*& findParent)
{
if (root == 0)
{
return false;
}
find = root;
findParent = 0;
while (find != 0 && find->element != val) // 如果找到或未找到,退出循环
{
findParent = find;
if (val < find->element) // 如果小于结点
{
find = find->leftChild; // 则查找结点的左子树
}
else // 如果大于结点
{
find = find->rightChild; // 则查找结点的右子树
}
}
return find != 0; // 返回是否找到
}
假设现在的需求不允许重复关键字。
插入过程要先进行查找:
// Author: https://blog.csdn.net/DarkRabbit
// Binary Search Tree
// BSTree插入(不可有重复Key)
// params:
// root: 根结点
// val: 待插入的值
// return:
// bool: 插入是否成功(即,树中是否已存在此值,如果存在返回false)
template<typename T>
bool BSTreeInsert(BSTreeNode<T>*& root, const T& val)
{
if (root == 0) // 如果空树
{
root = new BSTreeNode<T>(val); // 创建根结点
return true;
}
BSTreeNode<T>* find = 0;
BSTreeNode<T>* findParent = 0;
if (!BSTreeFind(root, val, find, findParent)) // 如果没有找到
{
find = new BSTreeNode<T>(val); // 创建结点
if (find->element > findParent->element)
{
findParent->rightChild = find; // 加入右子树
}
else
{
findParent->leftChild = find; // 加入左子树
}
return true;
}
return false; // 值已存在
}
在二叉排序树删除结点时,必须将因删除而断开的结点重新链接,这样才能保证二叉排序树的完整性。
在重新链接时,要额外注意链接位置;一般情况下,为了防止降低性能:
// Author: https://blog.csdn.net/DarkRabbit
// Binary Search Tree
// BSTree删除
// params:
// root: 根结点
// val: 待插入的值
// return:
// bool: 删除是否成功
template<typename T>
bool BSTreeDelete(BSTreeNode<T>*& root, const T& val)
{
if (root == 0)
{
return false; // 树为空树
}
BSTreeNode<T>* find = 0;
BSTreeNode<T>* findParent = 0;
if (!BSTreeFind(root, val, find, findParent))
{
return false; // 没有找到结点
}
BSTreeNode<T>* linkedNode = 0;
if (find->leftChild == 0) // 左子树空
{
linkedNode = find->rightChild; // 链接右子树
find->rightChild = 0;
}
else if (find->rightChild == 0) // 右子树空
{
linkedNode = find->leftChild; // 链接左子树
find->leftChild = 0;
}
else // 左右都不空,链接右子树中序遍历第一个结点
{
BSTreeNode<T>* tmpParent = 0; // 临时变量,父结点
linkedNode = find->rightChild;
while (linkedNode->leftChild != 0) // 中序遍历第一个结点
{
tmpParent = linkedNode;
linkedNode = linkedNode->leftChild;
}
linkedNode->leftChild = find->leftChild; // 顶替,链接左子树
// 如果 tmpParent 为空,则说明 linkedNode 就是 find->rightChild
// 不为空,则说明中间还有结点,需要断开 linkedNode 的右子树,并链接它们
if (tmpParent != 0 && linkedNode->rightChild != 0)
{
tmpParent->leftChild = linkedNode->rightChild; // 重新链接断开点
linkedNode->rightChild = find->rightChild; // 顶替,链接右子树
}
find->leftChild = 0;
find->rightChild = 0;
}
if (findParent != 0) // 如果删除的不是根结点
{
// 链接它们
if (findParent->leftChild == find)
{
findParent->leftChild = linkedNode;
}
else
{
findParent->rightChild = linkedNode;
}
}
delete find; // 销毁 find
return true;
}
举例说明,我们删除一个数字26,用它右子树中序第一个结点30替代它:
断开33与30,30与32的链接,并将33和32链接;
断开26与22,26与33的链接,链接30与22,30与33;
链接50与30。
// Author: https://blog.csdn.net/DarkRabbit
// Binary Search Tree
#include "binary_search_tree.h"
#include
using namespace std;
using namespace BinaryTrees;
typedef BSTreeNode<int>* BSTree;
// 打印访问结点
void PrintVisitedBSTreeElement(BSTreeNode<int>& node)
{
cout << node.element << ' ';
}
int main()
{
BSTree tree = 0;
cout << "二叉排序树:插入顺序 { 50, 26, 78, 92, 33, 67, 22, 30, 32 }" << endl;
vector<int> vals = { 50, 26, 78, 92, 33, 67, 22, 30, 32 };
for (int i = 0; i < vals.size(); i++)
{
BSTreeInsert(tree, vals[i]);
}
cout << "中序遍历:";
BSTreeInorderTraversal(tree, PrintVisitedBSTreeElement);
cout << endl;
cout << "删除 26" << endl;
if (BSTreeDelete(tree, 26))
{
cout << "中序遍历:";
BSTreeInorderTraversal(tree, PrintVisitedBSTreeElement);
cout << endl;
}
cout << "删除 33" << endl;
if (BSTreeDelete(tree, 33))
{
cout << "中序遍历:";
BSTreeInorderTraversal(tree, PrintVisitedBSTreeElement);
cout << endl;
}
delete tree;
//system("pause"); VC++
return 0;
}
二叉排序树:插入顺序 { 50, 26, 78, 92, 33, 67, 22, 30, 32 }
中序遍历:22 26 30 32 33 50 67 78 92
删除 26
中序遍历:22 30 32 33 50 67 78 92
删除 33
中序遍历:22 30 32 50 67 78 92
请按任意键继续. . .