概述
查找问题
是计算机中非常重要的基础问题,在查找时,还经常需要支持插入
和删除
,通常这一问题的方法有:
数据结构 |
查找 |
插入 |
删除 |
无序数组 |
O(n) |
O(n) |
O(n) |
有序数组 |
O(logn) |
O(n) |
O(n) |
尽管使用有序数组可以将查找操作优化到O(logn)
,但是在插入和删除时仍然为O(n)
。
使用二叉搜索树可以将三种操作均优化到O(logn)
。
一颗二叉搜索树(Binary Search Tree)满足以下性质:
- 他是一颗二叉树
- 左子树上所有节点的值都小于其根节点的值
- 右子树上所有节点的值都大于其根节点的值
须要注意的是,二叉搜索树中不允许出现重复值。
此外,根据其性质,容易知道二叉搜索树的子树也是二叉搜索树。例如,下面这棵树就是一个二叉搜索树:
而这棵树则不是二叉搜索树:
复杂度分析
二叉搜索树在平均情况下,即树保持相对平衡(其左右子树的高度接近)的情况下,插入、删除、操作均为O(logn)
,因为此时的树高度接近log(n)
,向下搜索的深度不会超过树的高度。
但是,二叉搜索树在极端情况下,会退化为链表,这种情况发生在顺序插入的数据已经有序的情况下,此时的树是一棵左斜树/右斜树,即只有左子树/右子树:
此时的BSTree高度为n
,三种操作均退化为O(n)
。
因此在实际应用中,使用平衡树来优化会有更好的效果。
三种操作
查找操作
很简单,从根节点开始向下迭代寻找key值,有四种情况:
- 如果当前节点值大于key,则继续搜索左子树
- 如果当前节点值小于key,则继续搜搜右子树
- 如果当前节点值等于key,则找到该值,查找成功
- 如果当前节点为空(NULL),则该值不存在,查找失败
插入操作
同样需要查找,从根节点开始向下根据key值进行迭代,有四种情况:
- 如果当前节点值大于key,则继续迭代左子树
- 如果当前节点值小于key,则继续迭代右子树
- 如果当前节点值等于key,则该值已存在,插入失败
- 如果当前节点为空(NULL),则该值不存在,并找到了空位,在此处创建新节点插入key,插入成功
删除操作
删除稍复杂些,因为删除节点后必须保证树不断开,并且保持二叉搜索树的性质。
同样需要查找,从根节点开始向下根据key值进行迭代,有四种情况:
- 如果当前节点值大于key,则继续迭代左子树
- 如果当前节点值小于key,则继续迭代右子树
- 如果当前节点值等于key,则找到该值,此时进行删除,删除成功
- 如果当前节点为空(NULL),则该值不存在,删除失败
删除结点N的方法:
- 如果N为叶子节点,则直接将其删除即可
- 如果N只有一个子节点(无论左右),则将该子节点直接替换掉N即可
- 如果N有两个子节点,那么此时有2种方法,选择其一即可:
- 找到N的右子树中的最小节点N2,替换掉N节点;然后,删除该最小节点
- 找到N的左子树中的最大节点N2,替换掉N节点;然后,删除该最大节点
代码实现
C语言实现
该代码使用链表来存储二叉树。
BSTree.h:
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
|
#ifndef ADT_BSTREE_H #define ADT_BSTREE_H #define bool int #define true 1 #define false 0 typedef int TreeElementType; typedef struct TreeNode { TreeElementType value; struct TreeNode *left; struct TreeNode *right; } TreeNode; typedef struct { TreeNode *root; int size; } BSTree_t, *BSTree;
BSTree newBSTree(void); static void destroyTreeRun(TreeNode *node); void destroyBSTree(BSTree T);
bool isEmptyTree(BSTree T); int treeSize(BSTree T); TreeElementType findMin(BSTree T); TreeElementType findMax(BSTree T);
bool find(BSTree T, TreeElementType key); bool insert(BSTree T, TreeElementType value); bool delete(BSTree T, TreeElementType value); #endif
|
BSTree.c:
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
| #include <stdio.h> #include <stdlib.h> #include "BSTree.h"
BSTree newBSTree(void) { BSTree T = (BSTree) malloc(sizeof(BSTree_t)); if (T == NULL) { printf("Error: create tree failed.\n"); return NULL; } T->root = NULL; T->size = 0; return T; } static void destroyTreeRun(TreeNode *node) { if (node == NULL) { return; } destroyTreeRun(node->left); destroyTreeRun(node->right); free(node); } void destroyBSTree(BSTree T) { destroyTreeRun(T->root); free(T); }
bool isEmptyTree(BSTree T) { return T->size == 0; } int treeSize(BSTree T) { return T->size; } TreeElementType findMin(BSTree T) { TreeNode *p = T->root; while (p->left != NULL) { p = p->left; } return p->value; } TreeElementType findMax(BSTree T) { TreeNode *p = T->root; while (p->right != NULL) { p = p->right; } return p->value; }
bool insert(BSTree T, TreeElementType value) { TreeNode *node = (TreeNode *) malloc(sizeof(TreeNode)); if (node == NULL) { printf("Error: create node failed.\n"); return false; } node->value = value; node->left = NULL; node->right = NULL; if (T->root == NULL) { T->root = node; T->size++; return true; } TreeNode *p = T->root; while (p != NULL) { if (value < p->value) { if (p->left == NULL) { p->left = node; T->size++; return true; } p = p->left; } else if (value > p->value) { if (p->right == NULL) { p->right = node; T->size++; return true; } p = p->right; } else { printf("Error: value %d already exists.\n", value); return false; } } return false; } bool delete(BSTree T, TreeElementType value) { TreeNode *p = T->root; TreeNode *parent = NULL; while (p != NULL) { if (value < p->value) { parent = p; p = p->left; } else if (value > p->value) { parent = p; p = p->right; } else { break; } } if (p == NULL) { printf("Error: value %d not found.\n", value); return false; } if (p->left == NULL && p->right == NULL) { if (parent == NULL) { T->root = NULL; } else if (parent->left == p) { parent->left = NULL; } else { parent->right = NULL; } free(p); T->size--; return true; } if (p->left == NULL || p->right == NULL) { TreeNode *child = p->left != NULL ? p->left : p->right; if (parent == NULL) { T->root = child; } else if (parent->left == p) { parent->left = child; } else { parent->right = child; } free(p); T->size--; return true; } TreeNode *q = p->right; TreeNode *q_parent = p; while (q->left != NULL) { q_parent = q; q = q->left; } p->value = q->value; q_parent->left = q->right; } else { q_parent->right = q->right; } free(q); T->size--; return true; } bool find(BSTree T, TreeElementType key) { TreeNode *p = T->root; while (p != NULL) { if (key < p->value) { p = p->left; } else if (key > p->value) { p = p->right; } else { return true; } } return false; }
|
测试main.c:
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
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include "BSTree.h" int main() { int nums[] = {5, 3, 6, 2, 4, 0, 8, 1, 7, 9}; BSTree T = newBSTree(); printf("insert 10 numbers into BSTree\n"); for (int i = 0; i < 10; ++i) { insert(T, nums[i]); } printf("size: %d\n", treeSize(T)); printf("min: %d\n", findMin(T)); printf("max: %d\n", findMax(T)); int key = 10; if (find(T, key)) { printf("find %d\n", key); } else { printf("not find %d\n", key); } key = 5; if (find(T, key)) { printf("find %d\n", key); } else { printf("not find %d\n", key); } printf("size: %d\n", treeSize(T)); while (!isEmptyTree(T)) { printf("%d ", findMin(T)); delete(T, findMin(T)); } printf("\nsize: %d\n", treeSize(T)); destroyBSTree(T); return 0; }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11
| $ ./ADT_BinaryTree.exe insert 10 numbers into BSTree size: 10 min: 0 max: 9 not find 10 find 5 size: 10 0 1 2 3 4 5 6 7 8 9 size: 0
|