算法导论-二叉查找树
目录
- 引言
- 二叉查找树
- 节点定义
- 查找操作
- 插入操作
- 删除操作
- 二叉查找树存在问题
- 完整源码
- 讨论区
- 参考资料
内容
二分查找、散列表查找;二分查找效率为Θ(lgn)、二分查找要求数组是静态的,即元素不能动态的插入和删除,否则会耗费较多的时间;散列表查找效率可以到达Θ(1),但是一般用于“等于性查找“,不能进行“范围查找”;本文介绍的二叉查找树,(1)能实现动态查找,查找效率仍能达到Θ(lgn);(2)也能方便的进行范围查找;
除了上面的查找,二叉查找树还能实现动态的插入和删除操作。并且期望效率能达到Θ(lgn)
Question 1、那么,什么是二叉查找树呢?
二叉查找树,也叫二叉搜索树、二叉排序树。是一种特殊的二叉树,数据结构可以用二叉链表结构表示;二叉查找树关键字满足如下特性:设x为二叉查找树中的一个结点。如果y是x的左子树中的一个结点,则key[y]≤key[x]。如果y是x的右子树中的一个结点,则key[x]≤key[y]
二叉查找树有一个很好的特性:中序遍历二叉查找树能输出一组有序的节点序列;其中,中序遍历运行时间为Θ(n)。
Question 2、二叉排序树各种操作的效率如何?
二叉树可以实现的操作包括:基本查询,查询最小、最大关键值、前驱、后继,单个节点的插入和删除操作。这些操作的效率与二叉树的高度成正比,一颗随机构造的二叉查找树的期望高度为O(lgn),从而基本动态集合的操作平均时间为Θ(lgn),注意这里只是期望效率,最坏情况下为Θ(n),在二叉查找树存在的问题小节中会进行讲解原因及如何改进使其最坏情况下达到Θ(lgn)
Github源码
BinarySearchTree.h(二叉查找树实现)
1 #include2 #include 3 using namespace std; 4 5 //二叉查找树节点定义,带模板 6 template <class T> 7 class BinarySearchTreeNode 8 { 9 public: 10 T key;//节点值,这里只有一个值,没有附带数据 11 BinarySearchTreeNode * parent;//指向双亲节点指针 12 BinarySearchTreeNode * left;//指向左孩子节点指针 13 BinarySearchTreeNode * right;//指向右孩子节点指针 14 }; 15 16 /* 17 二查查找树功能实现类,包括节点查找,最大、最小节点查找, 18 前驱、后继节点查找,插入节点,删除节点功能实现,带模板 19 */ 20 template <class T> 21 class BinarySearchTree 22 { 23 public: 24 BinarySearchTree(); 25 void tree_insert(const T& elem); 26 int tree_remove(const T& elem); 27 BinarySearchTreeNode * tree_search(const T& elem)const; 28 BinarySearchTreeNode * tree_minmum(BinarySearchTreeNode * root)const; 29 BinarySearchTreeNode * tree_maxmum(BinarySearchTreeNode * root)const; 30 BinarySearchTreeNode * tree_successor(const T& elem) const; 31 BinarySearchTreeNode * tree_predecessor(const T& elem)const; 32 bool empty() const; 33 void inorder_tree_traverse()const; 34 BinarySearchTreeNode * get_root()const { return root; } 35 void transplant(BinarySearchTreeNode * u, BinarySearchTreeNode * v); 36 private: 37 BinarySearchTreeNode * root; 38 }; 39 /*构造函数*/ 40 template <class T> 41 BinarySearchTree ::BinarySearchTree() 42 { 43 root = NULL; 44 } 45 /*二叉查找树插入操作实现 46 实现思路:插入结点的位置对应着查找过程中查找不成功时候的结点位置, 47 因此需要从根结点开始查找带插入结点位置,找到位置后插入即可 48 */ 49 template <class T> 50 void BinarySearchTree ::tree_insert(const T& elem) 51 { 52 if (!empty())//判断二叉树是否为空,非空则找到合适节点插入 53 { 54 BinarySearchTreeNode *pnode = root; 55 BinarySearchTreeNode *qnode = NULL; 56 BinarySearchTreeNode *newnode = new BinarySearchTreeNode ;//创建新节点 57 newnode->key = elem;//插入节点值 58 newnode->parent = NULL;//初始化双亲节点、左右孩子节点 59 newnode->left = NULL; 60 newnode->right = NULL; 61 while (pnode)//非空,查找插入节点位置 62 { 63 qnode = pnode; 64 if (pnode->key > elem)//当前节点值大于待插入节点值,向左子树查找 65 pnode = pnode->left; 66 else//当前节点值不大于待插入节点值,向右子树查找 67 pnode = pnode->right; 68 } 69 if (qnode->key > elem)//当前节点值大于待插入节点值,则插入到左孩子节点 70 qnode->left = newnode; 71 else//否则插入到右孩子节点 72 qnode->right = newnode; 73 newnode->parent = qnode;//设置新插入节点的双亲节点 74 } 75 else//二叉树为空,则插入节点为根节点 76 { 77 root = new BinarySearchTreeNode ; 78 root->key = elem; 79 root->parent = NULL; 80 root->left = NULL; 81 root->right = NULL; 82 } 83 } 84 /*删除节点调用的子过程transplant,用一颗子树v替换另外一颗子树u并成为其双亲的孩子节点 85 输入:u-被替换的子树指针,v-新替换的子树指针 86 输出:void 87 */ 88 template <class T> 89 void BinarySearchTree ::transplant(BinarySearchTreeNode * u, BinarySearchTreeNode * v) 90 { 91 if (u->parent == NULL) root = v; 92 else if (u == u->parent->left) u->parent->left=v; 93 else u->parent->right = v; 94 95 if (v != NULL) v->parent = u->parent; 96 } 97 /*节点删除实现 98 输入:要删除的节点的关键字 99 输出:删除成功返回0 100 不存在此关键字则返回-1 101 */ 102 template <class T> 103 int BinarySearchTree ::tree_remove(const T&elem) 104 { 105 BinarySearchTreeNode *pnode;//当前节点 106 BinarySearchTreeNode *snode;//双亲节点、后继节点 107 pnode = tree_search(elem);//查找待删除节点 108 if (!pnode) return -1;//待删除节点不存在,返回-1 109 if (pnode->left == NULL)//左孩子不存在,用右孩子直接替换待删除节点(这个右孩子可以是NULL,也可以不是NULL) 110 transplant(pnode, pnode->right); 111 else if (pnode->right == NULL)//只存在左孩子,用左孩子直接替换待删除节点 112 transplant(pnode,pnode->left); 113 else//左右孩子都存在 114 { 115 snode = tree_minmum(pnode->right);//求待删除节点的后继(因右孩子存在,所以一定是右子树中最小的节点) 116 if (snode->parent != pnode)//后继不是待删除节点的右孩子, 117 { 118 transplant(snode, snode->right);//snode是后继节点,所以无左子树,右子树替换掉snode 119 snode->right = pnode->right;//pnode的右孩子成为snode的右孩子 120 snode->right->parent = snode; 121 } 122 transplant(pnode, snode);//snode替换掉pnode 123 snode->left = pnode->left;//处理snode的左子树,及左子树的双亲关系 124 snode->left->parent = snode; 125 } 126 return 0; 127 128 } 129 /*查找search实现 130 输入:待查找的数据elem 131 输出:查找成功,返回指向该节点的指针; 132 待查找数值不存在,返回空指针 133 */ 134 template <class T> 135 BinarySearchTreeNode * BinarySearchTree ::tree_search(const T& elem)const 136 { 137 BinarySearchTreeNode *pnode = root;//节点指针,指向根节点 138 //非递归查找过程 139 while (pnode)//节点非空 140 { 141 if (pnode->key == elem)//查找成功:指针指向节点关键值等于待查找数值,退出while 142 break; 143 else if (pnode->key > elem)//当前节点值大于查找的elem,则在当前节点左孩子二叉树中进行查找 144 pnode = pnode->left; 145 else //当前节点值小于查找的elem,则在当前节点右孩子二叉树中进行查找 146 pnode = pnode->right; 147 } 148 149 return pnode; 150 } 151 152 /*查找最小关键字 153 输入:指向要查找的二叉树的根节点的指针 154 输出:返回指向最小关键字节点的指针 155 */ 156 template <class T> 157 BinarySearchTreeNode * BinarySearchTree ::tree_minmum(BinarySearchTreeNode * root)const 158 { 159 BinarySearchTreeNode *pnode = root;//节点指针,指向根节点 160 161 while (pnode->left)//从根结点开始,沿着各个节点的left指针查找下去,直到遇到NULL时结束 162 pnode = pnode->left; 163 164 return pnode; 165 } 166 /*查找最d大关键字 167 输入:指向要查找的二叉树的根节点的指针 168 输出:返回指向最大关键字节点的指针 169 */ 170 template <class T> 171 BinarySearchTreeNode * BinarySearchTree ::tree_maxmum(BinarySearchTreeNode * root)const 172 { 173 BinarySearchTreeNode *pnode = root;//节点指针,指向根节点 174 175 while (pnode->right)//从根结点开始,沿着各个节点的left指针查找下去,直到遇到NULL时结束 176 pnode = pnode->right; 177 178 return pnode; 179 } 180 /*求节点后继 181 输入:当前节点的值 182 输出: 当前节点的后继节点的指针 183 */ 184 template <class T> 185 BinarySearchTreeNode * BinarySearchTree ::tree_successor(const T& elem) const 186 { 187 BinarySearchTreeNode * pnode = tree_search(elem);//查找值为elem的节点,返回指针保存到pnode 188 BinarySearchTreeNode * parentnode=NULL;//定义双亲节点 189 if (pnode != NULL)//当前节点存在 190 { 191 if (pnode->right)//当前节点存在右孩子,则后继为右子树的最小关键字节点 192 return tree_minmum(pnode->right); 193 parentnode = pnode->parent;//不存在右孩子,取双亲节点 194 /* 195 //双亲节点不为空,并且当前节点时双亲节点的右孩子时,一直寻找双亲节点, 196 //直到双亲节点为空,或者当前节点是双亲节点的左孩子 197 */ 198 while (parentnode && pnode == parentnode->right) 199 { 200 pnode = parentnode; 201 parentnode = parentnode->parent; 202 } 203 if (parentnode)//双亲节点不为空,返回 204 return parentnode; 205 else//为空,无后继节点 206 return NULL; 207 } 208 return NULL;//当前节点不存在 209 } 210 /*求节点前驱 211 输入:当前节点的值 212 输出: 当前节点的前驱节点的指针 213 */ 214 template <class T> 215 BinarySearchTreeNode * BinarySearchTree ::tree_predecessor(const T& elem)const 216 { 217 BinarySearchTreeNode * pnode = tree_search(elem);//查找值为elem的节点,返回指针保存到pnode 218 BinarySearchTreeNode * parentnode;//定义双亲节点 219 if (pnode != NULL)//当前节点存在 220 { 221 if (pnode->right) 222 return tree_maxmum(pnode->right);//当前节点存在左孩子,则后继为左子树的最大关键字节点 223 parentnode = pnode->parent;//不存在左孩子,取双亲节点 224 /* 225 //双亲节点不为空,并且当前节点是双亲节点的左孩子时,一直寻找双亲节点, 226 //直到双亲节点为空,或者当前节点是双亲节点的右孩子 227 */ 228 while (parentnode && pnode == parentnode->left) 229 { 230 pnode = parentnode; 231 parentnode = pnode->parent; 232 } 233 if (parentnode)//双亲节点不为空,返回 234 return parentnode; 235 else//为空,无后继节点 236 return NULL; 237 } 238 return NULL; 239 } 240 /*判断二叉树查找树是否为空 241 输出:若为空,返回true 242 非空,返回false 243 */ 244 template <class T> 245 bool BinarySearchTree ::empty() const 246 { 247 return (NULL == root); 248 } 249 /*二叉查找树中序遍历非递归实现 250 实现思路:借助栈完成 251 */ 252 template <class T> 253 void BinarySearchTree ::inorder_tree_traverse()const 254 { 255 if (NULL != root) 256 { 257 stack *> s; 258 BinarySearchTreeNode *ptmpnode; 259 ptmpnode = root;//指向根节点 260 while (NULL != ptmpnode || !s.empty())//当前节点不空,或者栈不空 261 { 262 if (NULL != ptmpnode)//当前节点不为空,入栈,置左孩子节点为当前节点 263 { 264 s.push(ptmpnode);//入栈 265 ptmpnode = ptmpnode->left;//置左孩子节点为当前节点 266 } 267 else//当前节点为空,弹出栈顶元素并访问该节点,然后置右孩子节点为当前节点 268 { 269 ptmpnode = s.top();//弹出栈顶元素 270 s.pop(); 271 cout << ptmpnode->key << " ";//访问该节点 272 ptmpnode = ptmpnode->right; //然后置右孩子节点为当前节点 273 } 274 } 275 } 276 }
Main.cpp(主测试函数)
1 #include"BinarySearchTree.h" 2 int main() 3 { 4 BinarySearchTree<int> bstree; 5 BinarySearchTreeNode<int>* ptnode, *proot; 6 bstree.tree_insert(32); 7 bstree.tree_insert(21); 8 bstree.tree_insert(46); 9 bstree.tree_insert(54); 10 bstree.tree_insert(16); 11 bstree.tree_insert(38); 12 bstree.tree_insert(70); 13 cout << "inorder tree walk is: "; 14 bstree.inorder_tree_traverse(); 15 proot = bstree.get_root(); 16 cout << "\nmax value is: " << bstree.tree_maxmum(proot)->key << endl; 17 cout << "min value is: " << bstree.tree_minmum(proot)->key<< endl; 18 ptnode = bstree.tree_search(38); 19 if (ptnode) 20 cout << "the element 38 is exist in the binary tree.\n"; 21 else 22 cout << "the element 38 is not exist in the binary tree.\n"; 23 cout << "the successor of 38 is: " << bstree.tree_successor(38)->key << endl; 24 cout << "the predecessor of 38 is:" << bstree.tree_predecessor(38)->key << endl; 25 if (bstree.tree_remove(46) == 0) 26 cout << "delete 46 successfully" << endl; 27 else 28 cout << "delete 46 failed" << endl; 29 cout << "inorder tree walk is: "; 30 bstree.inorder_tree_traverse(); 31 exit(0); 32 }
运行结果:
堆排序是一颗完全二叉树,排序效率为Θ(nlgn),需要构造大顶堆或者小顶堆。边调整堆边交换元素排序。完成后进行层序遍历即可得到有序序列;
(3)二叉排序树可以不是完全二叉树,堆排序是一颗完全二叉树。
http://www.cnblogs.com/Anker/archive/2013/01/28/2880581.html
【2】http://www.cnblogs.com/huangxincheng/archive/2012/07/21/2602375.html
【3】《算法导论》