zoukankan      html  css  js  c++  java
  • 《算法导论》— Chapter 12 二叉查找树

    查找树是一种数据结构,它支持多种动态集合操作,包括Search、Minimum、Maximum、PreDecessor、Successor、Insert、Delete等。它既可以用作字典,也可以用作优先级队列;在二叉查找树(Binary Search Tree)上执行基本操作的时间与树的高度成正比,对于一颗含有n个结点的完全二叉树,基本操作的最坏情况运行时间为floor(logn)
    本章讨论二叉查找树的基本性质以及上面提及的基本操作的实现。

    GitHub 程序实现代码

    1 二叉查找树

    1.1 性质

    如下图所示,一颗二叉查找树是按照二叉树结构来组织的。这样的树一般用链表结构表示,每一个结点是一个对象,包含关键字key、父亲结点parent、左儿子结点left以及右儿子结点right四个属性。
    图12-1 二叉查找树
    明显的,对于二叉查找树中关键字的存储方式总是满足这样的性质:
    x为二叉查找树中的一个结点,如果yx左子树中的一个结点,则y>key<=x>key,如果yx右子树中的一个结点,则y>key>=x>key

    1.2 基本操作

    1.2.1 遍历

    根据二叉查找树的性质,可以用一个递归算法按照排列顺序依次输出所有关键字,这就是中序遍历,遍历顺序为根、左子树、右子树。同样的,有前序遍历,根的关键字在左右子树之前输出;后序遍历,根的关键字在其左右子树之后输出。

    1.2.2 查找

    对于二叉查找树,最常见的操作就是查找树中的某个关键字。查找操作同样采用递归的形式实现,其复杂度等于树的高度。
    操作过程如下图所示:
    图12-2 二叉查找树查询操作

    1.2.3 求最大、最小关键字

    对于用作优先级队列的结构,求最大最小关键字是必不可少的操作。
    要查找二叉查找树中的最小关键字,根据树的性质,只要从根节点开始,沿着各个结点的left指针查找下去,直到遇到NULL为止。
    同理,要查找二叉查找树中的最大关键字,根据树的性质,只要从根节点开始,沿着各个结点的right指针查找下去,直到遇到NULL为止。
    这两个操作的运行时间都是O(h)

    1.2.4 求前驱、后继

    我们知道,中序遍历二叉查找树得到的是一组有序序列,有时候需要求指定结点的前驱与后继。对于给定结点x的后继结点是具有大于或等于x>key中关键字的最小结点;同理,对于给定结点x的前驱结点是具有小于x>key中关键字的最大结点。根据二叉查找树的性质,不用对关键字做任何比较就可以得到给定结点的前驱和后继结点。

    1.2.5 插入

    插入和删除操作会引起整个二叉查找树表示的集合的动态变化。要反应出这种变化,就要修改数据结构,但是在修改的同时,还要保持整棵树的性质不变。
    将新值插入到一颗二叉查找树中的过程如下:

    1.2.6 删除

    相对于插入操作,删除更加复杂一些,下图详细展示了删除不同结点需要的步骤:
    图12-4 二叉查找树删除操作
    对高度为h的二叉查找树,动态集合操作Insert与Delete的运行时间都是O(n)

    2 二叉查找树程序实现

    下面给出的程序实现,包括了所有以上提及的基本操作:
    (1)BinarySearchTree.h

    #ifndef _BINARYSEARCHTREE_H_
    #define _BINARYSEARCHTREE_H_
    
    #include <iostream>
    
    typedef struct BSTNode{
        BSTNode *left;
        BSTNode *right;
        BSTNode *parent;
    
        int key;
    
        BSTNode(int data) : left(NULL), right(NULL), parent(NULL), key(data){}
    };
    
    class BinarySearchTree{
    public:
        BinarySearchTree();
        ~BinarySearchTree();
    
        //插入删除操作
        void Insert(int data);
        BSTNode *Delete(int data);
        BSTNode *root;
    };
    
    //查找操作
    BSTNode *Search(BSTNode * node, int data);
    
    //遍历操作
    void InOrderWalk(BSTNode * node);
    void PreOrderWalk(BSTNode * node);
    void PostOrderWalk(BSTNode * node);
    
    //查询最大最小值
    BSTNode *Maximum(BSTNode * node);
    BSTNode *Minimum(BSTNode * node);
    
    //查找前驱与后继
    BSTNode *PreDecessor(BSTNode *node);
    BSTNode *Successor(BSTNode *node);
    
    #endif

    (2)BinarySearchTree.cpp

    #include "BinarySearchTree.h"
    #include <iostream>
    
    BinarySearchTree::BinarySearchTree()
    {
        root = NULL;
    }
    
    BinarySearchTree::~BinarySearchTree()
    {
        delete root;
    }
    
    //向二分查找树中插入数据data
    void BinarySearchTree::Insert(int data)
    {
        BSTNode *node = new BSTNode(data);
        BSTNode *p = root, *q = NULL;
        while (p != NULL)
        {
            q = p;
            if (p->key > data)
                p = p->left;
            else
                p = p->right;
        }
        node->parent = q;
        if (q == NULL)
            root = node;
        else if (q->key > data)
            q->left = node;
        else
            q->right = node;
    }
    
    //从二分查找树中删除数据
    BSTNode *BinarySearchTree::Delete(int data)
    {
        BSTNode *node = Search(root, data);
        BSTNode *ret , *tmp;
    
        if (node == NULL)
            return node;
    
        //如果目标结点只有一个子女则删除该结点,否则删除其后继结点
        if (node->left == NULL || node->right == NULL)
            ret = node;
        else
            ret = Successor(node);
    
        //如果被删结点有左右孩子,将其链接到被删结点的父节点
        if (ret->left != NULL)
            tmp = ret->left;
        else
            tmp = ret->right;
    
        if (tmp != NULL)
            tmp->parent = ret->parent;
        //如果被删结点的父节点为空,则说明要删的是根节点
        if (ret->parent == NULL)
            root = tmp;
        else if (ret == ret->parent->left)
            ret->parent->left = tmp;
        else
            ret->parent->right = tmp;
    
        if (ret != node)
            node->key = ret->key;
    
        return ret;
    }
    
    //查找以node结点为根的子树中值为data的结点
    BSTNode *Search(BSTNode * node, int data)
    {
        if (node == NULL || node->key == data)
            return node;
        if (data < node->key)
            return Search(node->left, data);
        else
            return Search(node->right, data);
    }
    
    //遍历操作
    void InOrderWalk(BSTNode * node)
    {
        if (node != NULL)
        {
            InOrderWalk(node->left);
            std::cout << node->key << "	";
            InOrderWalk(node->right);
        }
    }
    
    void PreOrderWalk(BSTNode * node)
    {
        if (node != NULL)
        {
            std::cout << node->key << "	";
            InOrderWalk(node->left);
            InOrderWalk(node->right);
        }
    }
    
    void PostOrderWalk(BSTNode * node)
    {
        InOrderWalk(node->left);
        InOrderWalk(node->right);
        std::cout << node->key << "	";
    }
    
    //查询最大最小值
    BSTNode *Maximum(BSTNode * node)
    {
        while (node->left != NULL)
            node = node->left;
        return node;
    }
    
    BSTNode *Minimum(BSTNode * node)
    {
        while (node->right != NULL)
            node = node->right;
        return node;
    }
    
    //查找前驱与后继
    BSTNode *PreDecessor(BSTNode *node)
    {
        if (node->left != NULL)
            return Maximum(node->left);
    
        BSTNode *p = node->parent;
        while (p != NULL && node == p->left)
        {
            node = p;
            p = node->parent;
        }
    
        return p;
    }
    
    
    BSTNode *Successor(BSTNode *node)
    {
        if (node->right != NULL)
            return Minimum(node->right);
        BSTNode *p = node->parent;
        while (p != NULL && node == p->right)
        {
            node = p;
            p = node->parent;
        }
    
        return p;
    }

    (3)main.cpp

    #include "BinarySearchTree.h"
    #include <iostream>
    #include <cstdlib>
    #include <ctime>
    
    using namespace std;
    
    const int MAX = 101;
    const int N = 10;
    
    int main()
    {
        BinarySearchTree *bst = new BinarySearchTree();
    
        //设置随机化种子,避免每次产生相同的随机数   
        srand(time(0));
    
        //构造一个随机数组成的二分查找树
        for (int i = 0; i < N; i++)
            bst->Insert(rand() % MAX);
    
        //遍历查找树
        cout << "先序遍历二分查找树bst:" << endl;
        PreOrderWalk(bst->root);
    
        //遍历查找树
        cout << endl << "中序遍历二分查找树bst:" << endl;
        InOrderWalk(bst->root);
    
        //遍历查找树
        cout << endl << "后序遍历二分查找树bst:" << endl;
        PostOrderWalk(bst->root);
    
        int x = 45;
        BSTNode *node = Search(bst->root, x);
        if (node)
        {
            cout << endl << "二分查找树bst中存在结点x = " << x << endl;
            bst->Delete(x);
    
            cout << endl << "删除二分查找树中结点x = " << x << "成功!" << endl;
    
            //遍历查找树
            cout << endl << "先序遍历二分查找树bst:" << endl;
            PreOrderWalk(bst->root);
    
            //遍历查找树
            cout << endl << "中序遍历二分查找树bst:" << endl;
            InOrderWalk(bst->root);
    
            //遍历查找树
            cout << endl << "后序遍历二分查找树bst:" << endl;
            PostOrderWalk(bst->root);
            cout << endl << endl;
        }
        else{
            cout << endl << "二分查找树bst中不存在结点x = " << x << endl;
            cout << endl << endl;
        }
    
        system("pause");
        return 0;
    }

    测试结果(查找失败的情况):
    测试结果
    测试结果(查找成功并删除):
    测试结果

    3 随机构造的二叉查找树

    在本章的最后介绍了一种随机构造二叉查找树的理论方法,这主要是针对普通二叉查找树基本操作运行时间O(h)考虑的,这种方式可以使得:
    一棵在n个关键字上随机构造的二叉查找树的期望高度为O(logn)
    这一部分感觉应用不多吧,木有仔细看,^||^~~

  • 相关阅读:
    付宇泽20190912-1 每周例行报告
    付宇泽20190912-3 词频统计
    付宇泽20190912-2 命令行
    付宇泽20190905-1 每周例行报告
    付宇泽20190905-2 博客作业
    付宇泽20190905-3 命令行和控制台编程
    罗杨美慧 20190905-3 命令行和控制台编程
    【Linux运维】Centos7上借助ansible搭建LVS+Keepalived
    【shell 练习1】编写Shell条件句练习
    【第四章】Shell 条件测试表达式
  • 原文地址:https://www.cnblogs.com/shine-yr/p/5214932.html
Copyright © 2011-2022 走看看