定义
二叉搜索树(Binary Search Tree)或称二叉查找树,也称二叉排序树(Binary Sort Tree)。它或者是一棵空树,或者是具有下列性质的二叉树:
二叉搜索树与普通二叉树相比,有一些优秀的特征或性质:
- 由于节点是有序排放的:左子树<根节点<右子树。故在查找一个节点的时候,只需先和根节点比较,再决定是进入左子树还是右子树查找。而普通二叉树需要一个一个地遍历。
- 查找、插入的时间复杂度是O(h),h是树的高度。即当树的高度尽量低(比较平衡)时,效率高。
算法解释
不得不说,非线性结构的操作确实难于线性结构的,有些算法的逻辑比较复杂。下面对代码中给出的部分算法进行解释,便于阅读。
- 构造方法:BinarySearchTree();建树的过程就是一个插入的过程,所以插入操作是重要的。
- 求叶子节点数:int leaf();按某种方式遍历树,若左右孩子皆为空,即为叶子节点。代码中是按中序遍历的。
- 查找指定节点:bool search(ElemType);根据二叉搜索树节点的分布特点,查找只需在左或右子树中进行,并且插入树中已有的节点也算插入失败。插入操作逻辑比较清楚,代码易看懂。
- 获取指定节点的前驱:BTNode* predecessor(ElemType);这个操作在普通二叉树中是没有的。在二叉搜索树中,某节点的前驱指的是中序遍历时的前驱。故该操作本质上是一个中序遍历的过程。稍微不同的是,在遍历的过程中需要记录最近一次遍历的节点plastVisit,并判断当前访问的节点是否是指定节点。若是,则返回plastVisit。
- 获取后继和获取前驱的道理是一样的。
- 获取最小节点:BTNode* minimum();二叉搜索树中的最小节点一定是位于左子树(如果存在)。于是,不断遍历左子树即可,比较简单。
- 获取最大节点:BTNode* maximum();二叉搜索树中的最大节点一定是位于右子树(如果存在)。于是,不断遍历右子树即可,比较简单。
- 插入节点:bool insertNode(ElemType);插入的过程本质上也是查找,需要记住的是:新节点会插入到叶子节点处。
- 遍历:void traverse();二叉搜索树的遍历可以是多样的,各种遍历方式也在上一篇二叉树中实现了,这里只给出中序遍历。因为,对一棵二叉搜索树进行中序遍历会得到节点从小到大的排序序列。
- 删除节点:bool deleteNode(ElemType);删除的规则是这样的:
- 若待删节点无左子树,则用其右子树的根节点替换它。
- 若待删节点有左子树,则在左子树中寻找中序遍历的最后一个节点,用该节点替换它。
删除规则比较好看懂,但具体实施时,细节繁多,很不容易。这也是所有操作中最复杂的。画图理解:
其它操作在上一篇二叉树中已有所解释,不再赘述。具体细节还得看代码,代码较长,建议以方法为单位来理解,
代码
类定义
- #include<iostream>
- #include<iomanip>
- #include<stack>
- #include<queue>
- using namespace std;
- typedef int ElemType;
- //二叉树节点
- class BTNode //Binary Tree Node
- {
- public:
- ElemType data;
- BTNode* lchild; //左孩子
- BTNode* rchild; //右孩子
- BTNode(ElemType d, BTNode* left = NULL, BTNode* right = NULL)
- :data(d), lchild(left), rchild(right){}
- };
- //二叉搜索树
- class BinarySearchTree
- {
- private:
- //树根
- BTNode* Root;
- //节点总数
- int size;
- public:
- //构造方法
- BinarySearchTree();
- //析构方法
- ~BinarySearchTree();
- //判断树空
- bool empty()
- {return Root == NULL;}
- //求节点总数
- int getSize()
- {return size;}
- //求叶子节点数
- int leaf();
- //查找
- bool search(ElemType);
- //获取父节点
- BTNode* parent(ElemType);
- //获取前驱
- BTNode* predecessor(ElemType);
- //获取后继
- BTNode* successor(ElemType);
- //获取最小节点
- BTNode* minimum();
- //获取最大节点
- BTNode* maximum();
- //插入新节点
- bool insertNode(ElemType);
- //删除节点
- bool deleteNode(ElemType);
- //中序遍历
- void traverse()
- {inOrderWithoutRecursion();}
- void inOrderWithoutRecursion();
- };
类实现
- //构造方法
- BinarySearchTree::BinarySearchTree()
- {
- size = 0;
- Root = NULL;
- ElemType data;
- cout << "建树,输入节点,输入0结束:";
- while (cin >> data && data)
- insertNode(data);
- }
- //析构方法
- BinarySearchTree::~BinarySearchTree()
- {
- if (!empty())
- {
- queue<BTNode*> q;
- q.push(Root);
- BTNode* p = NULL;
- while (!q.empty())
- {
- p = q.front();
- q.pop();
- //左孩子不为空,则左孩子入队
- if (p->lchild)
- q.push(p->lchild);
- //右孩子不为空,则右孩子入队
- if (p->rchild)
- q.push(p->rchild);
- //释放内存
- delete p;
- }
- }
- }
- //求叶子节点数
- int BinarySearchTree::leaf()
- {
- int num = 0;
- //按中序遍历
- if (!empty())
- {
- stack<BTNode*> s;
- BTNode* p = Root;
- while (!s.empty() || p)
- {
- if (p)
- {
- s.push(p);
- p = p->lchild;
- }
- else
- {
- p = s.top();
- s.pop();
- //左右子树均为空,则为叶子节点
- if (p->lchild == NULL && p->rchild == NULL)
- num++;
- p = p->rchild;
- }
- }
- }
- return num;
- }
- //查找
- bool BinarySearchTree::search(ElemType data)
- {
- if (!empty())
- {
- BTNode* p = Root;
- while (p)
- {
- if (data == p->data)
- return true;
- else if (data < p->data)
- p = p->lchild;
- else
- p = p->rchild;
- }
- }
- //树空或查找失败
- return false;
- }
- BTNode* BinarySearchTree::parent(ElemType data)
- {
- if (!empty())
- {
- //根节点的父节点为空
- if (Root->data == data)
- return NULL;
- stack<BTNode*> s;
- BTNode* p = Root;
- while (!s.empty() || p)
- {
- if (p)
- {
- s.push(p);
- p = p->lchild;
- }
- else
- {//左子树访问完后,访问右子树
- p = s.top();
- s.pop();
- if ((p->lchild && p->lchild->data == data) || (p->rchild && p->rchild->data == data))
- return p;
- p = p->rchild;
- }
- }
- }
- return NULL;
- }
- //获取前驱
- BTNode* BinarySearchTree::predecessor(ElemType data)
- {
- BTNode* pcur, *plastVisit;
- pcur = plastVisit = NULL;
- if (!empty())
- {
- stack<BTNode*> s;
- pcur = Root;
- while (!s.empty() || pcur)
- {
- if (pcur)
- {
- //plastVisit = pcur;
- s.push(pcur);
- pcur = pcur->lchild;
- }
- else
- {
- pcur = s.top();
- s.pop();
- if (pcur->data == data)
- return plastVisit;
- else
- plastVisit = pcur;
- pcur = pcur->rchild;
- }
- }
- }
- return plastVisit;
- }
- //获取后继
- BTNode* BinarySearchTree::successor(ElemType data)
- {
- BTNode* pcur = NULL;
- pcur = Root;
- if (!empty())
- {
- stack<BTNode*> s;
- while (!s.empty() || pcur)
- {
- if (pcur)
- {
- s.push(pcur);
- pcur = pcur->lchild;
- }
- else
- {
- pcur = s.top();
- s.pop();
- if (pcur->data == data)
- return pcur->rchild;
- pcur = pcur->rchild;
- }
- }
- }
- //空树
- return NULL;
- }
- //获取最小节点
- BTNode* BinarySearchTree::minimum()
- {
- //最小节点在左子树最下边
- if (!empty())
- {
- BTNode* p = Root;
- while (p->lchild)
- p = p->lchild;
- return p;
- }
- //树空
- return NULL;
- }
- //获取最大节点
- BTNode* BinarySearchTree::maximum()
- {
- //最大节点在右子树最下边
- if (!empty())
- {
- BTNode* p = Root;
- while (p->rchild)
- p = p->rchild;
- return p;
- }
- //树空
- return NULL;
- }
- //插入新节点
- bool BinarySearchTree::insertNode(ElemType data)
- {
- /*
- 新节点都会被插入到叶子处
- 插入一般不会失败,除非是插入了重复节点。
- */
- if (Root == NULL)
- {
- Root = new BTNode(data);
- size++;
- return true;
- }
- else
- {
- BTNode* p = Root;
- while (true)
- {
- if (data < p->data)
- {
- //如果有左子树,则继续遍历左子树
- if (p->lchild)
- p = p->lchild;
- else
- {//否则,插入节点,下同
- p->lchild = new BTNode(data);
- break;
- }
- }
- else if (data > p->data)
- {
- if (p->rchild)
- p = p->rchild;
- else
- {
- p->rchild = new BTNode(data);
- break;
- }
- }
- else//遇到重复节点
- return false;
- }
- //插入新节点成功,节点总数加一
- size++;
- return true;
- }
- }
- //删除节点
- bool BinarySearchTree::deleteNode(ElemType data)
- {
- /*
- 删除规则
- 1.若待删节点无左子树,则用其右子树的根节点替换它。
- 2.若待删节点有左子树,则在左子树中寻找中序遍历的最后一个节点,用该节点替换它。
- */
- if (!empty())
- {
- //树中无此节点,删除失败
- if (!search(data))
- return false;
- /*
- p:待删结点
- Parent:待删除节点的父节点
- temp:替换节点
- tempp:替换节点的父节点
- */
- BTNode* p, *Parent, *temp, *tempp;
- p = Parent = temp = tempp = NULL;
- //获取待删除节点的父节点
- Parent = parent(data);
- //根据父节点,确定待删结点
- if (Parent->lchild && Parent->lchild->data == data)
- p = Parent->lchild;
- else
- p = Parent->rchild;
- //如果左子树不为空,查找其中序遍历的最后一个节点
- if (p->lchild)
- {
- temp = p->lchild;
- while (temp->rchild)
- {
- tempp = temp;
- //不断遍历右子树
- temp = temp->rchild;
- }
- //如果p的左孩子即是替换节点
- if (tempp == NULL)
- p->lchild = temp->lchild;
- else//替换节点的左子树作为其父节点的右子树(这句难以理解,需要多想想)
- tempp->rchild = temp->lchild;
- //替换节点继承待删结点的左右孩子
- temp->lchild = p->lchild;
- temp->rchild = p->rchild;
- }
- else
- temp = p->rchild;
- //替换节点替换掉待删结点(这也是为什么需要找到待删结点的父节点)
- if (Parent == NULL) //待删结点恰为根节点
- Root = temp;
- else if (Parent->lchild == p) //待删结点本身处于左子树
- Parent->lchild = temp;
- else//待删结点本身处于右子树
- Parent->rchild = temp;
- //删除待删结点
- delete p;
- //节点总数减一
- size--;
- return true;
- }
- //树空
- return false;
- }
- //中序遍历
- void BinarySearchTree::inOrderWithoutRecursion()
- {
- if (!empty())
- {
- stack<BTNode*> s;
- BTNode* p = Root;
- while (!s.empty() || p)
- {
- if (p)
- {
- s.push(p);
- p = p->lchild;
- }
- else
- {
- p = s.top();
- s.pop();
- cout << setw(4) << p->data;
- p = p->rchild;
- }
- }
- cout << endl;
- }
- }
主函数
- int main()
- {
- cout << "******二叉搜索树***by David***" << endl;
- BinarySearchTree tree;
- cout << "中序遍历" << endl;
- tree.traverse();
- cout << "树中节点总数 " << tree.getSize() << endl;
- cout << "叶子节点数 " << tree.leaf() << endl;
- BTNode* p = NULL;
- p = tree.minimum();
- p ? cout << "最小节点是 " << p->data << endl : cout << "树空!" << endl;
- p = tree.maximum();
- p ? cout << "最大节点是 " << p->data << endl : cout << "树空!" << endl;
- ElemType data = 2;
- cout << endl << "查找节点 " << data << endl;
- if (tree.search(data))
- {
- cout << "节点 " << data << " 查找成功!" << endl;
- p = tree.predecessor(data);
- p ? cout << "节点 " << data << " 的前驱是 " << p->data << endl : cout << "无前驱!" << endl;
- p = tree.successor(data);
- p ? cout << "节点 " << data << " 的后继是 " << p->data << endl : cout << "无后继!" << endl;
- }
- else
- cout << "节点 " << data << "不在树中!" << endl;
- data = 6;
- cout << endl <<"删除节点 " << data << endl;
- if (tree.deleteNode(data))
- {
- cout << "删除成功!" << endl;
- cout << "中序遍历" << endl;
- tree.traverse();
- cout << "树中节点总数 " << tree.getSize() << endl;
- cout << "叶子节点数 " << tree.leaf() << endl;
- data = 5;
- cout << endl << "查找节点 " << data << endl;
- if (tree.search(data))
- {
- cout << "节点 " << data << " 查找成功!" << endl;
- p = tree.predecessor(data);
- p ? cout << "节点 " << data << " 的前驱是 " << p->data << endl : cout << "无前驱!" << endl;
- p = tree.successor(data);
- p ? cout << "节点 " << data << " 的后继是 " << p->data << endl : cout << "无后继!" << endl;
- }
- else
- cout << "节点 " << data << "不在树中!" << endl;
- }
- else
- cout << "删除失败!" << endl;
- cout << endl;
- system("pause");
- return 0;
- }
运行
算法优化
插入算法的一个优化版本
- //插入新节点
- bool BinarySearchTree::insertNode(ElemType data)
- {
- BTNode *parent, *child;
- parent = NULL;
- child = Root;
- while (child)
- {
- parent = child;
- if (data < child->data)
- child = child->lchild;
- else if (data > child->data)
- child = child->rchild;
- else//插入相同关键字的节点,返回false
- return false;
- }
- //此时parent要么为空,要么就是叶子节点
- if (parent == NULL)//空树
- Root = new BTNode(data);
- else if (data < parent->data)
- parent->lchild = new BTNode(data);
- else
- parent->rchild = new BTNode(data);
- size++;
- return true;
- }