- 二叉树(binary tree)是一种树型数据结构,其中它的每个节点最多有两个孩子(可以没有,也可以只有一个)。
- 二叉搜索树(binary search tree)是一种特殊的二叉树,对每个节点,其左孩子/左子树的关键字值该节点的关键字值;其右孩子/右子树的关键字值都大于该节点的关键字值。
- 二叉搜索树的平均深度为。其中,为节点总数。
- 二叉搜索树的主要操作有:创建(初始化)二叉树,查找关键字Find,返回最大最小关键字FindMax, FindMin,向树中插入元素Insert,删除节点Delete,遍历二叉树。
- 涉及到二叉树的操作有两种方法实现:递归实现和非递归实现。
一、二叉(搜索)基本数据结构
- 树是由一个个节点构成,节点的构成成一般为:该节点包含的关键字值(data),left指针指向其左孩子,right指针指向其右孩子。
- 节点结构的一般声明如下:
typedef struct _TreeNode { // 定义二叉查找树的结构 ElemType data; // 数据 struct _TreeNode* left; // 指向左孩子指针 struct _TreeNode* right; // 指向其右孩子指针 }TreeNode, *SearchBTree; // TreeNode表示节点别称,*SearchBTree表示一个指向树(子树)根节点的指针
二、初始化树
- 一般初始化树为一个(TreeNode类型的)空指针。
- 初始化树可以和树的销毁工作合并成MakeEmpty()函数,递归地实现如下。
SearchBTree EmptyTree(SearchBTree T) // 初始化、构造一颗空树/销毁一颗树 { if (!T) { EmptyTree(T->left); // 递归地释放空间 EmptyTree(T->right); delete T; } return nullptr; }
- 根据树的结构特点,比较要插入元素X值与节点关键字的大小,递归地插入(一般递归实现比较简单)。
- 当X值小于某个节点的值,则递归地往其左子树中插入X;当X值大于某个节点的值,则递归地往其右子树中插入X。
- 递归的终止条件是节点为空,申请一个新节点关键字为X插入到树中。
void Insert(SearchBTree &T, ElemType x) { if (!T) { TreeNode* pT = new TreeNode; // 申请节点空间 pT->data = x; // 为节点赋值 pT->left = pT->right = nullptr; T = pT; // 将pT赋给T } else { if (x < T->data) // 如果x小于某个结点的数据 Insert(T->left, x); // 递归地在其左子树上寻找空结点插入 else if (x > T->data) // 如果x大于某个结点的数据 Insert(T->right, x); // 递归地在其左子树上寻找空结点插入 } }
- 在树中查找某个关键字值,并返回其所在节点。
- 递归地进行:从根节点开始,当前节点大于其查找的值,则递归地再其左子树中查找。否则,在其右子树中递归地查找。
- 递归终止的条件是:查找到该关键字所在节点并返回。返回空结点,表示树中没有要查找的关键字值。
TreeNode* Find(SearchBTree T, ElemType x) // 查找树中是否有值x { if (T == nullptr) // 没有找到,返回空指针 return nullptr; if (x < T->data) // 递归查找 return Find(T->left, x); else if (x > T->data) return Find(T->right, x); else return T; // 查找到了,则返回T指针 }
- 查找树中的最小值
- 1.递归实现:递归地查找其左子树。
TreeNode* FindMin(SearchBTree T) // 查找最小值,递归实现 { if (T->left == nullptr) return T; else return FindMin(T->left); }
- 2.非递归实现:从根节点开始,循环遍历每个结点的左孩子,发现左孩子为空,则停止并返回该结点。
TreeNode* FindMin2(SearchBTree T) // 查找最小值,非递归实现 { while (T->left != nullptr) T = T->left; return T; }
- 查找树中的最大值
- 递归实现:递归地查找其右子树。
TreeNode* FindMax(SearchBTree T) // 查找最大值 { if (T->right == nullptr) return T; else return FindMax(T->right); }
- 分两种情况:
- 1.删除的节点是树叶节点(没孩子):则直接删除。
- 2.删除的节点是(子树的)根节点:也分两种情况:① 该节点有一个孩子,则直接替换为其孩子节点,然后删除释放其自身。
- ② 该节点有两个孩子,先把该节点的关键字值替换为其右子树的最左边孩子的关键字值(最小值)。然后再删除其右子树自身的最小值。
void Delete(SearchBTree &T, ElemType x) // 删除指定元素 { TreeNode *tmpNode = new TreeNode; // 创建一个临时结点 if (T == nullptr) // 没有x存在 cout << "404 Not Found\n"; else if (x < T->data) // x小于某个结点值 Delete(T->left, x); // 递归地从其左子树删除x else if (x > T->data) // x大于某个结点的值 Delete(T->right, x); // 递归地从其右子树删除x else // 找到了x { if (T->left&&T->right) // x所在结点有两个孩子的情况 { tmpNode = FindMin(T->right); // 将临时结点赋值为其右子树中具有最小值的结点(即最左端的结点) T->data = tmpNode->data; // 移动数据至要删除的目标结点 Delete(T->right, tmpNode->data); // 删除其右子树中值为x的结点 } else { // x所在结点只有一个孩子和没有孩子的情况 tmpNode = T; // 要删除的结点T赋给临时结点tmpNode if (T->left == nullptr) // 若当前结点的左孩子为空 T = T->right; // 则,将当前结点赋值为其右孩子 else if (T->right == nullptr) // 若当前结点的右孩子为空 T = T->left; // 则,将当前结点赋值为其左孩子 else delete tmpNode; // 否则当前结点为叶子结点(没有孩子),则直接释放 } } }
- 二叉搜索树主要有三种遍历方式:先根(序)遍历,中根(序)遍历,后根(序)遍历
- 1.先根遍历:先访问根节点,再递归地遍历其左子树,最后再递归地遍历其右子树
void PreorderPrint(SearchBTree T) // 先根(序)遍历二叉树并打印出结点值 { if (T != nullptr) { cout << T->data << " "; PreorderPrint(T->left); PreorderPrint(T->right); } }
- 1.中根遍历:先递归地遍历其左子树,然后再访问根节点,最后再递归地遍历其右子树
void InorderPrint(SearchBTree T) // 中根(序)遍历二叉树并打印出结点值 { if (T != nullptr) { InorderPrint(T->left); cout << T->data << " "; InorderPrint(T->right); } }
- 3.后根遍历:先递归地遍历其左子树,再递归地遍历其右子树,最后再访问根节点
void PostorderPrint(SearchBTree T) // 中根(序)遍历二叉树并打印出结点值 { if (T != nullptr) { PostorderPrint(T->left); PostorderPrint(T->right); cout << T->data << " "; } }
int main() { const ElemType rawdata[] = { 19, 7, 9, 15, 23, 39, 4, 2, 75, 100, 43, 58 }; SearchBTree myTree = new TreeNode; myTree = EmptyTree(myTree); // 初始化树 for (int i = 0;i < sizeof(rawdata) / sizeof(ElemType);i++) { Insert(myTree, rawdata[i]); // 向树中插入给定数据 } cout << "The preorder print of the tree is: \n"; PreorderPrint(myTree); cout << endl; cout << "The inorder print of the tree is: \n"; InorderPrint(myTree); cout << endl; cout << "The postorder print of the tree is: \n"; PostorderPrint(myTree); cout << endl; cout << "Input a value: "; ElemType x; cin >> x; if (Find(myTree, x)) cout << x << "is in the tree.\n"; else cout << x << "is not in the tree.\n"; TreeNode* TmpNode = new TreeNode; TmpNode = FindMin(myTree); cout << "The minimum value in the tree is: " << TmpNode->data << endl; TmpNode = FindMax(myTree); cout << "The maximum value in the tree is: " << TmpNode->data << endl; cout << "Input a value to delete: "; cin >> x; Delete(myTree, x); cout << "Now, the inorder print of the tree is: \n"; InorderPrint(myTree); cout << "\nDestroy the tree..." << endl; EmptyTree(myTree); system("pause"); return 0; }
The preorder print of the tree is: 19 7 4 2 9 15 23 39 75 43 58 100 The inorder print of the tree is: 2 4 7 9 15 19 23 39 43 58 75 100 The postorder print of the tree is: 2 4 15 9 7 58 43 100 75 39 23 19 Input a value to search: 13 13 is not in the tree. The minimum value in the tree is: 2 The maximum value in the tree is: 100 Input a value to delete: 58 Now, the inorder print of the tree is: 2 4 7 9 15 19 23 39 43 75 100 Destroy the tree...
文章来源: 二叉搜索树及其相关操作