二叉搜索树

    xiaoxiao2023-10-10  161

    二叉搜索树

    1、基本性质2、基本操作遍历先序遍历 查找查找给定值key查找最小关键字查找最大关键字查找某个节点的后继(successor)查找某个结点的前驱(predecessor) 插入删除

    1、基本性质

    二叉搜索树 (Binary Search Tree, BST) 又称二叉排序树或二叉查找树。 二叉搜索树的左子树均小于根节点,右子树均大于根节点。

    // 节点定义 struct BSTNode { int key; BSTNode* lchild; BSTNode* rchild; BSTNode* parent; }

    2、基本操作

    遍历

    先序遍历:访问根节点;遍历左子树;遍历右子树。 中序遍历:遍历左子树;访问根节点;遍历右子树。 (由于二叉搜索树具有 左子树<根节点<右子树 的性质,中序遍历二叉搜索树的结果是所有节点的升序序列。) 后序遍历:遍历左子树;遍历右子树;访问根节点。

    遍历的时间复杂度:O(n)。(因为每个节点都要被访问一次。)

    先序遍历

    // 递归实现 void preOrderRecursively(BSTNode* pNode) { if (pNode != NULL){ cout << pNode->key << " "; preOrderRecursively(pNode->lchild); preOrderRecursively(pNode->rchild); } } // 迭代实现???? void preOrderIteratively(BSTNode* pNode) { ??? }

    查找

    查找给定值key

    从根节点开始查找给定的key,若key<根节点,则继续查找左子树;若key>根节点,则继续查找右子树。

    查找的时间复杂度:O(h)。(h为二叉搜索树的高度。) 可以发现当二叉搜索树近似平衡的时候,查找的时间复杂度为O(logn);当二叉搜索树完全不平衡的时候,查找的时间复杂度为O(n)。

    查找最小值,为二叉搜索树的最左端的节点;查找最大值,为二叉搜索树最右端的节点。

    // 递归实现 BSTNode* searchRecursively(BSTNode* pNode, int key) { if (pNode == NULL || pNode->key == key) { return pNode; } if (key < pNode->key) { return searchRecursively(pNode->lchild, key); } else { return searchRecursively(pNode->rchild, key); } } // 迭代实现 BSTNode* searchIteratively(BSTNode* pNode, int key) { while(pNode != NULL && pNode->key != key){ if (key < pNode->key){ pNode = pNode-> lchild; } else { pNode = pNode->rchild; } } return pNode; }

    查找最小关键字

    二叉树的最左节点。

    BSTNode* searchMinimum(BSTNode* x) { while(x != NULL && x->lchild != NULL){ x = x->lchild; } return x; }

    查找最大关键字

    二叉树的最右节点。

    BSTNode* searchMaximum(BSTNode* x) { while(x != NULL && x->rchild != NULL){ x = x->rchild; } return x; }

    查找某个节点的后继(successor)

    给定一棵二叉搜索树的结点,查找二叉树中序遍历的后继(即排序序列中该结点的后一个元素)。由于二叉搜索树的性质,我们可以通过没有任何关键字的比较来确定后继。

    若该结点是最大关键字,则其后继为NULL;否则,       若该节点的右子树非空,其后继为该结点的右子树的最左结点。       若右子树为空,             若该结点为其父节点的左孩子,则该结点的后继为其父节点;             若该结点为其父节点的右孩子,则该结点的后继为其祖先结点中左孩子为该节点的祖先结点的结点。 BSTNode* searchSuccessor(BSTNode* x) { // 查找结点x的后继 // 若右子树非空,则后继为其右子树的最左结点(即右子树的最小关键字) if (x->rchild != NULL) return searchMinimum(x->rchild); BSTNode* y = x->parent; // 若该结点为其父节点的左孩子,则不进入循环,直接返回其父节点 while(y != NULL && y->rchild == x){ // 直到找到一个祖先结点的左孩子为其祖先结点,停止循环 // 若找到根节点仍找不到这样的结点,根节点的父节点为NULL,此时退出循环,返回NULL,即此时该结点为二叉搜索树中的最大关键字,无后继结点。 x = y; // 记住祖先结点 y = y->parent; // 继续查找 } return y;

    查找某个结点的前驱(predecessor)

    查找前驱与查找后继的情况是对称的。

    若该结点是二叉搜索树中的最小关键字,则前驱为NULL;否则,       若该结点存在左子树,则其前驱为左子树中的最右结点(searchMaximum);       若该结点的左子树为空,             若该结点为其父节点的右孩子,则其前驱为其父节点;             若该结点为其父节点的左孩子,则其前驱结点为其祖先结点中右孩子仍为其祖先结点的结点。 BSTNode* searchPredecessor(BSTNode* x) { if (x==NULL) return x; if (x->lchild != NULL) return searchMaximum(x->lchild); BSTNode* y = x->parent; while(y != NULL && x == y->lchild){ x = y; y = y->parent; } return y; }

    插入

    插入过程首先从根开始遍历,在遍历过程中我们要记录双亲,然后通过关键字的比较,决定向左向右移动,直到找到一个NULL,就是要插入的位置。 注意:插入结点后,新结点总是作为一个新的叶子结点而存在。

    插入的时间复杂度:O(logn)

    void insert(BSTNode *& T, int key) { // 注意这里的函数参数使用了指针的引用,意味着在函数内部改变其值会导致函数外其值的改变,可以用于当插入的结点为根结点时(例如树空时),根节点需要改变。 BSTNode* pNode = (BSTNode*)malloc(sizeof(BSTNode)); pNode->key = key; pNode->lchild = NULL; pNode->rchild = NULL; pNode->parent = NULL; BSTNode* x = T; BSTNode* y = NULL; while(x != NULL){ y = x; // y用于记住父节点 if (key < x->key) x = x->lchild; else x = x->rchild; } pNode->parent = y; if (y == NULL) T = pNode; // 插入的结点为根节点 else if (key < y->key) y->lchild = pNode; else y->rchild = pNode; }

    删除

    需要保证删除后不改变二叉搜素树的性质。

    若待删除结点z没有孩子结点,只需要简单删除,将其父节点的孩子结点置为NULL以替换结点z;若z只有一个孩子结点,只需以这个孩子结点替换结点z;若z有两个孩子,寻找z的后继y,令y替换z,z原来的左子树称为y新的左子树,z原来的右子树称为y新的右子树(注意后继y一定没有左孩子):       若z的后继为z的右孩子,则用y替换z,y的左子树为原来z的左子树,y的右子树保持不变;       若z的后继y不是z的右孩子,先用y的右孩子替换y,然后再用y替换z。

    删除的时间复杂度为:O(h)。h为二叉搜素树的高度。

    // 用一棵以v为根的子树替换一棵以u为根的子树,u的父节点变为v的父节点,u的父节点的孩子结点变为v void transplant(BSTNode* &T, BSNode* u, BSTNode* v) { // 需要修改u的父节点的孩子以及v的父节点 if (u->parent == NULL) T = v; else if (u == u->parent->lchild) u->parent->lchild = v; else u->parent->rchild = v; if (v != NULL) v->parent = u->parent; } // 将结点z从以T为根节点的二叉搜索树中删除 BSTNode* delete(BSTNode* &T, BSTNode* z) { BSTNode* y = NULL; // 若存在0个或1个孩子结点,用其孩子结点替换 if (z->lchild == NULL) transplant(T, z, z->lchild); else if (z->rchild == NULL) transplant(T, z, z->lchild); else { // 存在两个孩子,则用其后继替换 y = searchMinimum(z->rchild); if (y->parent != z) { transplant(T, y, y->rchild); y->rchild = z->rchild; y->rchild->parent = y; } transplant(T, z, y); y->lchild = z->lchild; y->lchild->parent = y; } return z; }

    参考资料: [1] [深入学习理解二叉搜索树(附详细讲解与实例分析)] (https://blog.csdn.net/qq_21396469/article/details/78419609)

    最新回复(0)