二叉搜索树

来源:互联网 发布:windows隐藏任务栏图标 编辑:程序博客网 时间:2024/05/01 03:53

定义

二叉搜索树(Binary Search Tree)或称二叉查找树,也称二叉排序树(Binary Sort Tree)。它或者是一棵空树,或者是具有下列性质的二叉树:
  1. 若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 左、右子树也分别为二叉搜索树;

性质

二叉搜索树与普通二叉树相比,有一些优秀的特征或性质:
  1. 由于节点是有序排放的:左子树<根节点<右子树。故在查找一个节点的时候,只需先和根节点比较,再决定是进入左子树还是右子树查找。而普通二叉树需要一个一个地遍历。
  2. 查找、插入的时间复杂度是O(h),h是树的高度。即当树的高度尽量低(比较平衡)时,效率高。

算法解释

不得不说,非线性结构的操作确实难于线性结构的,有些算法的逻辑比较复杂。下面对代码中给出的部分算法进行解释,便于阅读。
  1. 构造方法:BinarySearchTree();建树的过程就是一个插入的过程,所以插入操作是重要的。
  2. 求叶子节点数:int leaf();按某种方式遍历树,若左右孩子皆为空,即为叶子节点。代码中是按中序遍历的。
  3. 查找指定节点:bool search(ElemType);根据二叉搜索树节点的分布特点,查找只需在左或右子树中进行,并且插入树中已有的节点也算插入失败。插入操作逻辑比较清楚,代码易看懂。
  4. 获取指定节点的前驱:BTNode* predecessor(ElemType);这个操作在普通二叉树中是没有的。在二叉搜索树中,某节点的前驱指的是中序遍历时的前驱。故该操作本质上是一个中序遍历的过程。稍微不同的是,在遍历的过程中需要记录最近一次遍历的节点plastVisit,并判断当前访问的节点是否是指定节点。若是,则返回plastVisit。
  5. 获取后继和获取前驱的道理是一样的。
  6. 获取最小节点:BTNode* minimum();二叉搜索树中的最小节点一定是位于左子树(如果存在)。于是,不断遍历左子树即可,比较简单。
  7. 获取最大节点:BTNode* maximum();二叉搜索树中的最大节点一定是位于右子树(如果存在)。于是,不断遍历右子树即可,比较简单。
  8. 插入节点:bool insertNode(ElemType);插入的过程本质上也是查找,需要记住的是:新节点会插入到叶子节点处。
  9. 遍历:void traverse();二叉搜索树的遍历可以是多样的,各种遍历方式也在上一篇二叉树中实现了,这里只给出中序遍历。因为,对一棵二叉搜索树进行中序遍历会得到节点从小到大的排序序列。
  10. 删除节点: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;elsep = 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;elseplastVisit = 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;elsep = 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;}elsetemp = 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;}elsecout << "节点 " << 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;}elsecout << "节点 " << data << "不在树中!" << endl;}elsecout << "删除失败!" << 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//插入相同关键字的节点,返回falsereturn false;}//此时parent要么为空,要么就是叶子节点if (parent == NULL)//空树Root = new BTNode(data);else if (data < parent->data)parent->lchild = new BTNode(data);elseparent->rchild = new BTNode(data);size++;return true;}

完整代码下载:二叉搜索树
    
若是有所帮助,顶一个哦!

专栏目录:
  • 数据结构与算法目录
  • c指针


1 0
原创粉丝点击