二叉排序树
一、定义
二叉排序树,又叫二叉查找树,它或者是一棵空树;或者是具有以下性质的二叉树:
1. 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2. 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3. 它的左右子树也分别为二叉排序树。
如下图所示:
二、二叉排序树的C++实现
1、结点定义
为简单起见,这里将结点的键值类型设为int.
class BSTNode { public: int key; //结点的值 BSTNode* left; //结点的左孩子 BSTNode* right; //结点的右孩子 BSTNode* parent; //结点的双亲 /*构造函数*/ BSTNode():parent(NULL) {} BSTNode(int key, BSTNode* left, BSTNode* right, BSTNode* parent) :key(key), left(left), right(right), parent(parent) {} };
2、二叉排序树的各种操作
class BSTree { private: BSTNode* root; //根节点 public: /*构造函数*/ BSTree() :root(NULL) {}; /*获取根节点*/ BSTNode* getRoot() {return root;} /*将键值key插入到二叉树中*/ void insert(int key); /*将结点插入到二叉树中*/ void insert(BSTNode*& root, BSTNode* node); /*先序遍历*/ void preOrder(BSTNode* root); /*中序遍历*/ void inOrder(BSTNode* root); /*后序遍历*/ void postOrder(BSTNode* root); /*查找二叉树中键值为key的结点并返回*/ BSTNode* search(BSTNode* node, int key); /*找出二叉树中键值最小的结点并返回*/ BSTNode* minimum(BSTNode* node); /*找出二叉树中键值最大的结点并返回*/ BSTNode* maximum(BSTNode* node); /*找到二叉树结点node的后继结点*/ BSTNode* successor(BSTNode* node); /*找到二叉树结点node的前驱结点*/ BSTNode* predecessor(BSTNode* node); /*移除键值为key的结点*/ BSTNode* remove(BSTNode*& root, int key); /*销毁二叉排序树*/ void destroy(BSTNode* root); };
3、插入
在二叉排序树进行插入操作时,每次插入的结点都是二叉排序树上新的叶子结点。可以将结点插入到一棵已经存在的二叉排序树上,也可以通过插入结点来构造一棵二叉排序树。
/* * 将结点插入到二叉树中 * * 参数说明: * root 二叉树的根结点 * node 要插入的结点 */ void BSTree::insert(BSTNode*& root, BSTNode* node) { BSTNode* y = NULL; BSTNode* x = root; /*找到要插入的位置*/ while (x != NULL) { y = x; if (node->key > x->key) x = x->right; else x = x->left; } /*插入结点*/ node->parent = y; if (y == NULL) root = node; else if(y->key > node->key) y->left = node; else y->right = node; } void BSTree::insert(int key)
{ BSTNode* node = new BSTNode(key, NULL, NULL, NULL); insert(root, node); }
4、遍历
二叉排序树的遍历同普通二叉树一样分为先序、中序、后序遍历,实现起来也没有差别。需要注意的是,中序遍历二叉排序树会得到一个关键字的有序序列。
4.1 先序遍历
/*先序遍历*/ void BSTree::preOrder(BSTNode* root) { if (root != NULL) { cout << root->key; preOrder(root->left); preOrder(root->right); } }
4.2 中序遍历
中序遍历二叉排序树会得到一个关键字的有序序列。
/*中序遍历*/ void BSTree::inOrder(BSTNode* root) { if (root != NULL) { inOrder(root->left); cout << root->key; inOrder(root->right); } }
4.3 后序遍历
/*后序遍历*/ void BSTree::postOrder(BSTNode* root) { if (root != NULL) { postOrder(root->left); postOrder(root->right); cout << root->key; } }
5、查找
当二叉树非空时,首先将待查找的键值与根节点的键值比较,若大于根节点的键值,则继续查找右子树,否则查找左子树。重复上述过程,直至查找成功返回找到的结点,否则返回空。
BSTNode* BSTree::search(BSTNode* node, int key) { if (node == NULL || node->key == key) return node; if (node->key < key) search(node->right, key); else search(node->left, key); }
6、最大值与最小值
在一棵非空二叉排序树中,最小值结点为最左下结点,最大值结点为最右下结点。
6.1 获取最小值结点
BSTNode* BSTree::minimum(BSTNode* node) { if (node->left == NULL) return node; minimum(node->left); }
6.2 获取最大值结点
BSTNode* BSTree::maximum(BSTNode* node) { if (node->right == NULL) return node; maximum(node->right); }
7、前驱结点与后继结点
结点node的前驱结点是所有键值小于node的结点中的最大结点,也就是node的左子树的最大结点;
结点node的后继结点是所有键值大于node的结点中的最小结点,也就是node的右子树的最小结点。
7.1 前驱结点
(1)若结点node的左子树非空,则左子树的最大结点即为node的前驱结点;
(2)若结点node的左子树为空
(2.1)若node为右孩子,则node的前驱结点为node的父结点;
(2.2)若node为左孩子,则查找结点node的最低的父结点,且该父结点要有右孩子,此最低父结点即为node的前驱结点。
/*查找结点node的前驱节点*/ BSTNode* BSTree::predecessor(BSTNode* node) { /*(1)左子树非空,返回左子树最大值结点*/ if (node->left != NULL) return maximum(node->left); /*(2)*/ BSTNode* pnode = node->parent; while (pnode != NULL&&node == pnode->left) { node = pnode; pnode = pnode->parent; } return pnode; }
7.2 后继结点
(1)若结点node的右子树非空,则右子树的最小结点即为node的后继结点;
(2)若结点node的右子树为空
(2.1)若结点node为左孩子,则node的后继结点即为其父结点;
(2.2)若结点node为右孩子,则查找node的最低的父结点,且该父结点要有左孩子,此最低父结点即为node的后继结点。
/*查找node的后继结点*/ BSTNode* BSTree::successor(BSTNode* node) { /*(1)右子树非空,返回右子树最小值结点*/ if (node->right != NULL) return minimum(node->right); /*(2)*/ BSTNode* pnode = node->parent; while (pnode != NULL&&node == pnode->right) { node = pnode; pnode = pnode->parent; } return pnode; }
8、删除结点
假设要删除的结点为*p(p为指向要删除结点的指针),其双亲结点为*f,不失一般性,可设*p是*f的左孩子。
(1)若*p结点为叶子结点,即PL和PR均为空,则只需修改f->left为空即可;
(2)若*p结点只有左子树PL或者只有右子树PR,这只需令PL和PR直接成为f的左孩子即可;
(3)若*p结点的左子树和右子树均不为空,在删去*p之后,为保持其他元素之间的相对位置不变,可以有两种做法:
(3.1)做法一:令*s为*p的左子树PL的最右结点,则令*p的左子树PL为*f的左子树,*p的右子树PR为*s的右子树;
(3.2)做法二:令*p的直接前驱(或直接后继)替代*p,然后再从二叉排序树中删除它的直接前驱(或直接后继)。
下面的代码当遇到情况(3)时,采用做法一:
/*获取要删除的结点并返回*/ BSTNode* BSTree::remove(BSTNode*& root, int key) { BSTNode* node = search(root, key); printf("%d\n", node->key); if (node != NULL) { if (node->left == NULL && node->right == NULL) //node为叶子结点 { if (node->parent == NULL) //要删除的结点为根结点 return node; else if (node->parent->left == node)//判断要删除点在双亲结点的左边还是右边 node->parent->left = NULL; else node->parent->right = NULL; } else if (node->left == NULL) //node左子树为空 { if (node->parent == NULL) //要删除的结点为根结点 { this->root = node->right; node->right->parent = NULL; } else if (node->parent->left == node)//判断要删除点在双亲结点的左边还是右边 node->parent->left = node->right; else node->parent->right = node->right; } else if (node->right == NULL) //node右子树为空 { if (node->parent == NULL) //要删除的结点为根结点 { this->root = node->left; node->left->parent = NULL; } else if (node->parent->left == node)//判断要删除点在双亲结点的左边还是右边 node->parent->left = node->left; else node->parent->right = node->left; } else //node左右子树均不为空 { BSTNode* lnode = node->left; //lnode初始为node左子树的根节点 while (lnode->right) //找到node左子树的最右结点赋值为lnode lnode = lnode->right; lnode->right = node->right; //将node的右子树变成lnode的右子树 node->right->parent = lnode; if (node->parent == NULL) //要删除的结点为根结点 { this->root = node->right; if (node->right->left != NULL) { BSTNode* leftDownNode = minimum(node->right); leftDownNode->left = node->left; node->left->parent = leftDownNode; } else { node->right->left = node->left; node->left->parent = node->right; } } else if (node->parent->left == node) //将node的左子树替换node的位置 { node->parent->left = node->left; node->left->parent = node->parent; } else if (node->parent->right == node) { node->parent->right = node->left; node->left->parent = node->parent; } } } return node; }
9、销毁
销毁二叉排序树与销毁普通二叉树没有区别,这里采用后序遍历的方式来销毁。
/*销毁二叉树*/ void BSTree::destroy(BSTNode* root) { if (root == NULL) return; destroy(root->left); destroy(root->right); delete root; }
三、全部程序
1、头文件bstree.h
#include <iostream> #include <cstdio> #include <cstring> using namespace std; class BSTNode { public: int key; //结点的值 BSTNode* left; //结点的左孩子 BSTNode* right; //结点的右孩子 BSTNode* parent; //结点的双亲 /*构造函数*/ BSTNode():parent(NULL) {} BSTNode(int key, BSTNode* left, BSTNode* right, BSTNode* parent) :key(key), left(left), right(right), parent(parent) {} }; class BSTree { private: BSTNode* root; //根节点 public: /*构造函数*/ BSTree() :root(NULL) {}; /*获取根节点*/ BSTNode* getRoot(); /*将键值key插入到二叉树中*/ void insert(int key); /*将结点插入到二叉树中*/ void insert(BSTNode*& root, BSTNode* node); /*先序遍历*/ void preOrder(BSTNode* root); /*中序遍历*/ void inOrder(BSTNode* root); /*后序遍历*/ void postOrder(BSTNode* root); /*查找二叉树中键值为key的结点并返回*/ BSTNode* search(BSTNode* node, int key); /*找出二叉树中键值最小的结点并返回*/ BSTNode* minimum(BSTNode* node); /*找出二叉树中键值最大的结点并返回*/ BSTNode* maximum(BSTNode* node); /*找到二叉树结点node的后继结点*/ BSTNode* successor(BSTNode* node); /*找到二叉树结点node的前驱结点*/ BSTNode* predecessor(BSTNode* node); /*移除键值为key的结点*/ BSTNode* remove(BSTNode*& root, int key); /*销毁二叉排序树*/ void destroy(BSTNode* root); }; BSTNode* BSTree::getRoot() { return root; } /*先序遍历*/ void BSTree::preOrder(BSTNode* root) { if (root != NULL) { cout << root->key; preOrder(root->left); preOrder(root->right); } } /*中序遍历*/ void BSTree::inOrder(BSTNode* root) { if (root != NULL) { inOrder(root->left); cout << root->key; inOrder(root->right); } } /*后序遍历*/ void BSTree::postOrder(BSTNode* root) { if (root != NULL) { postOrder(root->left); postOrder(root->right); cout << root->key; } } BSTNode* BSTree::search(BSTNode* node, int key) { if (node == NULL || node->key == key) return node; if (node->key < key) search(node->right, key); else search(node->left, key); } BSTNode* BSTree::minimum(BSTNode* node) { if (node->left == NULL) return node; minimum(node->left); } BSTNode* BSTree::maximum(BSTNode* node) { if (node->right == NULL) return node; maximum(node->right); } /*查找node的后继结点*/ BSTNode* BSTree::successor(BSTNode* node) { /*(1)右子树非空,返回右子树最小值结点*/ if (node->right != NULL) return minimum(node->right); /*(2)*/ BSTNode* pnode = node->parent; while (pnode != NULL&&node == pnode->right) { node = pnode; pnode = pnode->parent; } return pnode; } /*查找结点node的前驱节点*/ BSTNode* BSTree::predecessor(BSTNode* node) { /*(1)左子树非空,返回左子树最大值结点*/ if (node->left != NULL) return maximum(node->left); /*(2)*/ BSTNode* pnode = node->parent; while (pnode != NULL&&node == pnode->left) { node = pnode; pnode = pnode->parent; } return pnode; } /* * 将结点插入到二叉树中 * * 参数说明: * root 二叉树的根结点 * node 要插入的结点 */ void BSTree::insert(BSTNode*& root, BSTNode* node) { BSTNode* y = NULL; BSTNode* x = root; /*找到要插入的位置*/ while (x != NULL) { y = x; if (node->key > x->key) x = x->right; else x = x->left; } /*插入结点*/ node->parent = y; if (y == NULL) root = node; else if(y->key > node->key) y->left = node; else y->right = node; } void BSTree::insert(int key) { BSTNode* node = new BSTNode(key, NULL, NULL, NULL); insert(root, node); } //BSTNode* BSTree::remove(BSTNode*& root, int key) //{ // BSTNode* x = NULL; // BSTNode* y = NULL; // // BSTNode* z = search(root, key); // if (z->left == NULL || z->right == NULL) // y = z; // else y = successor(z); // // if (y->left != NULL) // x = y->left; // else x = y->right; // // if (x != NULL) // x->parent = y->parent; // // if (y->parent == NULL) // root = x; // else if (y->parent->left == y) // y->parent->left = x; // else if (y->parent->right == y) // y->parent->right = x; // // if (y != z) // z->key = y->key; // // return y; //} /*获取要删除的结点并返回*/ BSTNode* BSTree::remove(BSTNode*& root, int key) { BSTNode* node = search(root, key); printf("%d\n", node->key); if (node != NULL) { if (node->left == NULL && node->right == NULL) //node为叶子结点 { if (node->parent == NULL) //要删除的结点为根结点 return node; else if (node->parent->left == node)//判断要删除点在双亲结点的左边还是右边 node->parent->left = NULL; else node->parent->right = NULL; } else if (node->left == NULL) //node左子树为空 { if (node->parent == NULL) //要删除的结点为根结点 { this->root = node->right; node->right->parent = NULL; } else if (node->parent->left == node)//判断要删除点在双亲结点的左边还是右边 node->parent->left = node->right; else node->parent->right = node->right; } else if (node->right == NULL) //node右子树为空 { if (node->parent == NULL) //要删除的结点为根结点 { this->root = node->left; node->left->parent = NULL; } else if (node->parent->left == node)//判断要删除点在双亲结点的左边还是右边 node->parent->left = node->left; else node->parent->right = node->left; } else //node左右子树均不为空 { BSTNode* lnode = node->left; //lnode初始为node左子树的根节点 while (lnode->right) //找到node左子树的最右结点赋值为lnode lnode = lnode->right; lnode->right = node->right; //将node的右子树变成lnode的右子树 node->right->parent = lnode; if (node->parent == NULL) //要删除的结点为根结点 { this->root = node->right; if (node->right->left != NULL) { BSTNode* leftDownNode = minimum(node->right); leftDownNode->left = node->left; node->left->parent = leftDownNode; } else { node->right->left = node->left; node->left->parent = node->right; } } else if (node->parent->left == node) //将node的左子树替换node的位置 { node->parent->left = node->left; node->left->parent = node->parent; } else if (node->parent->right == node) { node->parent->right = node->left; node->left->parent = node->parent; } } } return node; } /*销毁二叉树*/ void BSTree::destroy(BSTNode* root) { if (root == NULL) return; destroy(root->left); destroy(root->right); delete root; }
2、测试文件bstree.cpp
1 #include "bstree.h" 2 3 int main() 4 { 5 int a[] = { 1, 5, 4, 3, 2, 6 }; 6 7 BSTree* tree = new BSTree(); 8 for (int i = 0; i < 6;i++) 9 tree->insert(a[i]); 10 11 cout << "先序遍历:"; 12 tree->preOrder(tree->getRoot()); 13 cout << endl; 14 15 cout << "中序遍历:"; 16 tree->inOrder(tree->getRoot()); 17 cout << endl; 18 19 cout << "后序遍历:"; 20 tree->postOrder(tree->getRoot()); 21 cout << endl; 22 23 cout << "最小值:"; 24 BSTNode* minNode = tree->minimum(tree->getRoot()); 25 if(minNode != NULL) 26 cout << minNode->key << endl; 27 28 cout << "最大值:"; 29 BSTNode* maxNode = tree->maximum(tree->getRoot()); 30 if (maxNode != NULL) 31 cout << maxNode->key << endl; 32 33 34 BSTNode* node = tree->search(tree->getRoot(), 6); 35 BSTNode* snode = tree->successor(node); 36 if (snode != NULL) 37 cout << snode->key << endl; 38 39 BSTNode* pnode = tree->predecessor(node); 40 if (pnode != NULL) 41 cout << pnode->key << endl; 42 43 BSTNode* root = tree->getRoot(); 44 BSTNode* dnode = tree->remove(root, 5); 45 cout << "删除" << dnode->key << "后先序遍历:" << endl; 46 if (dnode) delete dnode; 47 tree->preOrder(tree->getRoot()); 48 cout << endl; 49 50 cout << "销毁二叉树" << endl; 51 tree->destroy(root); 52 }
3、结果
先序遍历:154326 中序遍历:123456 后序遍历:234651 最小值:1 最大值:6 5 删除5后先序遍历: 14326 销毁二叉树
四、参考
1、http://www.cnblogs.com/skywang12345/p/3576373.html
2、 严蔚敏、吴伟民《数据结构》