二叉排序树又称二叉搜索树,若它的左子树不为空,则左子树上所有结点的值都小于根节点的值。
若它的右子树不为空,则右子树上所有结点的值都大于根结点的值。
二叉排序树的左右子树也分别为二叉排序树。
如何存储二叉排序树
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #define eletype int
const int max_nodes = 100;
struct Node { eletype data; int *left, *right; };
Node tree[max_nodes]; int root = 0, size = 1;
|
二叉排序树的操作
查找
从根节点开始查找,如果大于根结点则向右查找,小于根结点则向左查找。 我们可以看下面的这个动画演示,在这个动画中要查找的是63。
在二又排序树的查找过程中,由于需要反复查询左右子树,可以考虑使用递归来实现查找操作。查找的递归解题思想如下:
如果树非空,则将目标值与当前结点的值进行比较。
如果目标值小于当前结点的值,则递归遍历左子树。
如果目标值大于当前结点的值,则递归遍历右子树。
如果目标值等于当前结点的值,则查找成功,返回该结点的索引值
1 2 3 4 5 6 7 8 9
| int searchtree(int index, eletype value) { if(index == 0) return 0; if(tree[index].data == value) return index; if(value < tree[index].data) return searchtree(tree[index].left, value); return searchtree(tree[index].right, value); }
|
插入
按照序列 {50, 25, 75, 89, 63, 37, 12} 建立二叉排序树(BST)。我们可以看下面的这个动画演示。
插入操作是向二又排序树中添加新结点的过程。从根结点开始,根据节点值与当前节点的大小关系逐级向下比较,插入操作的基木思路是:
如果待插入结点的值小于当前结点的值,则继续在当前结点的左子树中查找。
如果待插入结点的值大于当前结点的值,则继续在当前结点的右子树中查找。
找到一个空结点,将待插入节点插入到该空结点的位置。
1 2 3 4 5 6 7 8 9 10 11 12 13
| int insert_tree(int index, eletype value) { if(index == 0) { tree[size].data = value; tree[size].left = nullptr; tree[size].right = nullptr; } if(key < tree[index].data) tree[index].left = insert_tree(tree[index].left, value); else if(key > tree[index].data) tree[index].right = insert_tree(tree[index].right, value); return index; }
|
遍历
二又树的遍历有三种,即前序遍历、中序遍历、后续遍历。一般使用中序遍历二叉排序树,即左根右的顺序,遍历操作的基本思路是:
如果当前结点索引为0,直接返回(空结点)。
递归遍历当前结点的左子树
输出当前结点的值
递归遍历当前结点的右子树
1 2 3 4 5 6 7 8
| void inorderTraversal(Node *index) { if (index == 0) { return; } inorderTraversal(index->left); cout << index->val << " "; inorderTraversal(index->right); }
|
删除
二叉树的删除操作比遍历、查询和插入操作更为复杂。删除结点可以分为三种情况。
第一种情况:删除的结点 P 为叶子结点
如果 P 结点为叶子结点,那么其左右子树都为空,也就是说删除此结点并不会破坏整棵树的结构,直接删除结点 P 即可。
以数组 {50, 25, 75, 89, 63, 37, 12} 为例,要删除叶子结点 63 的叶子结点。我们可以看下面的动画演示。
第二种情况:删除的结点 P 只有左孩子 P1 或者右孩子 P2
用它的那棵唯一的子树直接顶替它原来的位置——不需要再去找什么前驱、后继,也用不着旋转、染色之类的花哨操作。具体步骤分两步:
把 P 的父结点 F 中指向 P 的那条指针(F.lchild 或 F.rchild)改成指向 P 的独苗孩子 C(C 可能是 P1 也可能是 P2)。
释放(或回收)P 结点本身。
就这么简单,时间复杂度 O(1),树高不变,BST 性质也天然保持。以数组 {47, 18, 80, 13, 29, 79, 90, 95} 为例,要删除结点 90。我们可以看下面的动画演示。
第三种情况:删除的节点P的左右子树都不为空
当待删结点 P 的左右子树均非空时,“直接顶替”会立刻破坏 BST 序,所以必须挑一个“合法替身”来接盘。
合法替身只有两类:
左子树里的最大结点——即 P 的前驱(in-order predecessor)。
右子树里的最小结点——即 P 的后继(in-order successor)。
任选其一即可,下面以前驱法为例给出完整流程(后继法对称)。
前驱法(in-order predecessor)删除步骤
记 P 的左子树根为 L。
在 L 的右链上一直向右走,直到右指针为空的结点 Q;
Q 就是 P 的直接前驱,且 Q 一定没有右子树(否则还能再往右)。
把 Q 的数据域复制到 P,完成“值替换”。
现在问题转化为“删除 Q”:
– 若 Q 是 L 本身(说明 L 没有右子树),则把 P 的左指针指向 L 的左子树;
– 否则 Q 是某个结点 R 的右孩子,把 R 的右指针指向 Q 的左子树(Q 最多只有左子树)。
释放 Q。
后继法(in-order successor)一句话总结
在右子树里一直往左走到头,把那个最小结点复制到 P,然后删除那个最小结点(它最多只有右子树),操作完全对称。
算法复杂度
以序列 {47, 18, 80, 13, 29, 79, 95} 为例,删除结点 80。我们可以看下面的动画演示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| Node* deleteNode(Node* index, int value) { if (index == NULL) { cout << "未找到值为 " << value << " 的节点" << endl; return NULL; } if (value < index->val) { index->left = deleteNode(index->left, value); } else if (value > index->val) { index->right = deleteNode(index->right, value); } else { if (index->left == NULL) { Node* temp = index->right; delete index; cout << "成功删除节点: " << value << endl; return temp; } else if (index->right == NULL) { Node* temp = index->left; delete index; cout << "成功删除节点: " << value << endl; return temp; } Node* temp = findMin(index->right); index->val = temp->val; index->right = deleteNode(index->right, temp->val); cout << "成功删除节点: " << value << endl; } return index; }
|
最后附上全部代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
| #include <bits/stdc++.h> using namespace std; struct TreeNode { int val; TreeNode* left; TreeNode* right; }; TreeNode* createNode(int value) { TreeNode* newNode = new TreeNode; newNode->val = value; newNode->left = NULL; newNode->right = NULL; return newNode; } void insertNode(TreeNode* &root, int key) { if (root == NULL) { root = createNode(key); return; } if (key < root->val) { insertNode(root->left, key); } else if (key > root->val) { insertNode(root->right, key); } else { cout << "值 " << key << " 已存在,不重复插入" << endl; } }
bool searchNode(TreeNode* root, int key) { if (root == NULL) { return false; } if (key == root->val) { return true; } else if (key < root->val) { return searchNode(root->left, key); } else { return searchNode(root->right, key); } } void inorderTraversal(TreeNode* root) { if (root == NULL) { return; } inorderTraversal(root->left); cout << root->val << " "; inorderTraversal(root->right); } TreeNode* findMin(TreeNode* root) { if (root == NULL) { return NULL; } while (root->left != NULL) { root = root->left; } return root; } TreeNode* deleteNode(TreeNode* root, int key) { if (root == NULL) { cout << "未找到值为 " << key << " 的节点" << endl; return NULL; } if (key < root->val) { root->left = deleteNode(root->left, key); } else if (key > root->val) { root->right = deleteNode(root->right, key); } else { if (root->left == NULL) { TreeNode* temp = root->right; delete root; cout << "成功删除节点: " << key << endl; return temp; } else if (root->right == NULL) { TreeNode* temp = root->left; delete root; cout << "成功删除节点: " << key << endl; return temp; } TreeNode* temp = findMin(root->right); root->val = temp->val; root->right = deleteNode(root->right, temp->val); cout << "成功删除节点: " << key << endl; } return root; } void freeTree(TreeNode* &root) { if (root == NULL) { return; } freeTree(root->left); freeTree(root->right); delete root; root = NULL; } int getHeight(TreeNode* root) { if (root == NULL) { return 0; } int leftHeight = getHeight(root->left); int rightHeight = getHeight(root->right); if (leftHeight > rightHeight) { return leftHeight + 1; } else { return rightHeight + 1; } } int countNodes(TreeNode* root) { if (root == NULL) { return 0; } int leftCount = countNodes(root->left); int rightCount = countNodes(root->right); return leftCount + rightCount + 1; }
void displayTree(TreeNode* root, string prefix, bool isLeft) { if (root == NULL) { return; } cout << prefix; cout << (isLeft ? "├──" : "└──" ); cout << root->val << endl; if (root->left != NULL || root->right != NULL) { displayTree(root->left, prefix + (isLeft ? "│ " : " "), true); displayTree(root->right, prefix + (isLeft ? "│ " : " "), false); } }
int main() { TreeNode* root = NULL; int choice, key; cout << "========== 二叉排序树操作系统 ==========" << endl; cout << "说明:二叉排序树(BST)具有以下性质:" << endl; cout << "1. 左子树所有节点值 < 根节点值" << endl; cout << "2. 右子树所有节点值 > 根节点值" << endl; cout << "3. 左右子树也都是BST" << endl << endl; while (true) { cout << "\n========== 操作菜单 ==========" << endl; cout << "1. 插入元素(支持批量插入)" << endl; cout << "2. 查找元素" << endl; cout << "3. 中序遍历(显示排序结果)" << endl; cout << "4. 删除节点" << endl; cout << "5. 显示树的高度" << endl; cout << "6. 显示节点总数" << endl; cout << "7. 显示树形结构" << endl; cout << "8. 清空整棵树" << endl; cout << "9. 退出程序" << endl; cout << "请输入选择 (1-9): "; cin >> choice; switch (choice) { case 1: { cout << "批量插入说明:输入多个数字,以空格分隔,最后输入-1结束" << endl; cout << "请输入要插入的元素(输入-1结束): "; while (cin >> key) { if (key == -1) { break; } insertNode(root, key); cout << "? 元素 " << key << " 插入成功" << endl; } cin.clear(); cin.ignore(1000, '\n'); break; } case 2: { if (root == NULL) { cout << "树为空,请先插入元素!" << endl; break; } cout << "请输入要查找的元素: "; cin >> key; if (searchNode(root, key)) { cout << "? 元素 " << key << " 存在于树中" << endl; } else { cout << "? 元素 " << key << " 不存在于树中" << endl; } break; } case 3: { cout << "中序遍历结果(升序排列): "; if (root == NULL) { cout << "树为空!" << endl; } else { inorderTraversal(root); cout << endl; } break; } case 4: { if (root == NULL) { cout << "树为空,无法删除!" << endl; break; } cout << "当前树的中序遍历: "; inorderTraversal(root); cout << endl; cout << "请输入要删除的元素: "; cin >> key; root = deleteNode(root, key); break; } case 5: { int height = getHeight(root); cout << "树的高度: " << height << endl; break; } case 6: { int count = countNodes(root); cout << "节点总数: " << count << endl; break; } case 7: { if (root == NULL) { cout << "树为空!" << endl; } else { cout << "树的形状:" << endl; displayTree(root, "", true); } break; } case 8: { if (root == NULL) { cout << "树已为空!" << endl; } else { freeTree(root); cout << "? 树已清空" << endl; } break; } case 9: { cout << "\n正在退出程序..." << endl; if (root != NULL) { freeTree(root); } cout << "感谢使用!" << endl; return 0; } default: { cout << "错误:请输入1-9之间的数字!" << endl; cin.clear(); cin.ignore(1000, '\n'); break; } } } return 0; }
|