二叉查找树

二叉树

树在数据结构里面是一种非常自然的数据结构,很多自然和人类社会的现象抽象出来都很自然地形成一棵树.例如,如果将一个组织机构的管理层次画在纸上,可能看上去就像一棵倒过来的数。
事实上,人类社会的很多管理层次架构都可以用树的形状来表示。而将这种结构在计算机中予以表示就是数据结构里面的数。那么数据结构是如何对树定义的呢?

树结构的定义

要得出树的定义,我们需要对树进行抽象,可以发现树由节点和连线构成。如果删除每个节点的语义,可以得出类似下图的抽象树结构。
这里写图片描述
显然,树是一个递归结构:从任意一个节点往下的所有节点又构成一棵抽象树。再加上一个数据结构可以为空。可以得出树的定义如下。
树的定义:
(1)一棵树要么为空;
(2)要么由一个根节点和一棵以上的(根节点的)子树组成。
下面进入本文的讨论对象:

二叉树

虽然每棵树在形状上大同小异,都输由节点和边构成无环路结构,但每个节点分叉多少还是有不同情况。而这种不同可以用来对树结构进行分类:分叉为二就是二叉树、分叉为三就是三叉树、···、分叉n就是n叉树。但由于一棵树里面的所有节点不一定都分叉一样,n叉树的定义只得修改为数里面分叉最多的节点的分叉数为n。
以分叉多少玩儿分类的树结构中最重要的就是二叉树,即每个节点最多只有两个分支。二叉树的重要性来源于生活中的二叉现象,例如,在生活中,很多决定都是所谓的二分的,即只有两个可能性:是与否,对与错,等等。而这种二分的决策过程用一棵树来抽象就是二叉树。
二叉树当然是一棵树,因此树的表示可以直接应用到二叉树上,只不过节点里的指针数量只有两个。因此,二叉树的节点结构更具有确定性。下面是二叉树定义。

typedef int nodeEntry;

struct Bnode        //二叉树节点结构
{   
    nodeEntry data; //数据成员
    Bnode *left;    //左子树
    Bnode *right;   //右子树
};

class BinTree
{
    Bnode *root;
public:
    BinTree();
    ~BinTree();
};

BinTree::BinTree()
{
    root = NULL;
}

BinTree::~BinTree()
{
    delete root;
}

二叉树的遍历

对于一棵树来说,常常需要对它的所有节点进行某种操作。这种对所有的节点进行检查的操作就是遍历。例如,如果将一个机构的人员隶属关系用一棵树表示,年终对每个人进行考核时即需要对这棵树的节点进行遍历。
遍历当然需要一个节点一个节点地进行。第一个碰到的接待是根节点。在遍历了根节点后有3项任务需要完成:
(1)访问本节点;
(2)遍历本节点地左子树;
(3)遍历本节点的右子树。
而这3项任务进行的顺序不同,就有不同的遍历模式。显然,3个元素进行顺序排列,共有6种情况。但由于左右对称的关系,实际上不同的遍历模式只有3种,分别是:前序遍历,中序遍历和后序遍历。
前序遍历就是将本节点放在最前面,也就是访问本节点后在遍历子树(先左后右)。
中序遍历就是将本节点的访问放在中间,先遍历左子树(先左后右)后再访问本节点。
后序遍历就是将本节点放在最后面,也就是遍历子树(先左后右)后在访问本节点。
这里的前、中、后都是从当前节点的视角来观察的。
下面就来实现二叉树的前序、中序、后序遍历。

typedef int nodeEntry;

struct Bnode        //二叉树节点结构
{   
    nodeEntry data; //数据成员
    Bnode *left;    //左子树
    Bnode *right;   //右子树
};

class BinTree
{
    Bnode *root;
    void recursivePreorder(Bnode *x);
    void recursiveInorder(Bnode *x);
    void recursivePostorder(Bnode *x);
public:
    BinTree();
    ~BinTree();
    void preorder();
    void inorder();
    void postorder();
};

BinTree::BinTree()
{
    root = NULL;
}

BinTree::~BinTree()
{
    delete root;
}

void BinTree::preorder()            //本函数对二叉树进行前序遍历,使用辅助函数recursivePreorder
{
    recursivePreorder(root);
}

void BinTree::recursivePreorder(Bnode *subRoot) //subRoot要么为空,要么指向一棵子二叉树
{
    if (subRoot != NULL)
    {
        cout << subRoot->data << " ";
        recursivePreorder(subRoot->left);
        recursivePreorder(subRoot->right);
    }
}

void BinTree::inorder()             //本函数对二叉树进行中序遍历,使用辅助函数recursiveInorder
{
    recursiveInorder(root);
}

void BinTree::recursiveInorder(Bnode *subRoot)//subRoot要么为空,要么指向一棵子二叉树
{
    if (subRoot != NULL)
    {
        recursiveInorder(subRoot->left);
        cout << subRoot->data << " ";
        recursiveInorder(subRoot->right);
    }
}

void BinTree::postorder()           //本函数对二叉树进行后序遍历,使用辅助函数recursivePostorder
{
    recursivePostorder(root);
}

void BinTree::recursivePostorder(Bnode *subRoot)//subRoot要么为空,要么指向一棵子二叉树
{
    if (subRoot != NULL)
    {
        recursivePostorder(subRoot->left);
        recursivePostorder(subRoot->right);
        cout << subRoot->data << " ";
    }
}

除了遍历一棵二叉树外,还可以对二叉树进行很多操作,例如往一棵树里插入一个节点,在一棵树里删除一个节点,查找一个特定的数据值项,计算二叉树的高度,查看一个二叉树是否为空,清除一棵树中所有的节点,等等。这些操作是每个数据结构都需要提供的,也是二叉树的基本操作。
其中判断为空,清除等都是比较简单的。那么如何实现二叉树的插入、查找和删除操作呢?显然要查找一个给定目标是否存在在于二叉树中,就需要遍历二叉树;要进行删除操作则需要进行查找。因此这两种操作的成本同样与二叉树的节点数量成正比。但插入操作则有所不同:不需要进行遍历,只需要玩一个反向(左或右)一直找下去,碰到第一个只有子节点的节点或叶子节点是即可进行插入。而插入操作的时间成本与树的高度成正比。
但是其实往一棵二叉树中插入一个节点存在模糊性:在寻找插入点时当然可以一直往左走,也可以一直往右走。但是也可以左右左右地走。因此,一个新的节点可以插入的地方很多,即任何一个数据项可作为另外一个数据项的子节点或父节点。这也是导致二叉树的查找成本与节点数成正比的原因。
解决这个问题的办法就是使用二叉查找树。

二叉查找树

由前面对二叉树操作的讨论可知,仅仅把数据组织成二叉树的形状并无太大意义。在这种二叉树结构里,很多操作具有模糊性或者成本高昂,如果一棵树里面的数据之间的关系与树的形状没有关联的话,想要在一棵树的结构中查找一个特定的数据值的节点就变得无从着手,唯一的办法就是对树进行遍历,而这是费时费力的,最好是能从树的形状对数据的语义进行某种判断。例如,用二叉树来存储一个有序列表,可以快速地进行查找插入和删除操作。这种结构就是二叉查找树。
从上面的分析可以很快给出二叉查找树的定义。
(1)它是一棵二叉树;
(2)要么为空,要么每个节点满足如下条件:
①如果该节点的左子树存在,该节点比其左子树的所有节点的值大;
②如果该节点的右子树存在,该节点比其右子树的所有节点的值小;
③该节点的子树本身又是一棵二叉查找树。

下面就来实现二叉查找树的各个函数。

#include<iostream>
using namespace std;

typedef int nodeEntry;
const int SUCCESS = 0;

struct Bnode        //二叉树节点结构
{
    nodeEntry data; //数据成员
    Bnode *left;    //左子树
    Bnode *right;   //右子树
};

class BinTree
{
    Bnode *root;
    void recursivePreorder(Bnode *x);
    void recursiveInorder(Bnode *x);
    void recursivePostorder(Bnode *x);
    int searchDelete(Bnode *subRoot, const nodeEntry target);
    int removeRoot(Bnode *subRoot);
    Bnode *searchNode(Bnode *subRoot, const nodeEntry target)const;
    int searchInsert(Bnode * &subRoot, const nodeEntry newData);
public:
    BinTree();
    ~BinTree();
    void preorder();
    void inorder();
    void postorder();
    bool empty()const;
    void clear();
    int height()const;
    int size()const;
    int insert(const nodeEntry x);
    int remove(const nodeEntry x);
    int search(const nodeEntry x)const;
protected:
    int count;
};

BinTree::BinTree()
{
    count = 0;
    root = NULL;
}

BinTree::~BinTree()
{
    delete root;
}

void BinTree::preorder()            //本函数对二叉树进行前序遍历,使用辅助函数recursivePreorder
{
    recursivePreorder(root);
}

void BinTree::recursivePreorder(Bnode *subRoot) //subRoot要么为空,要么指向一棵子二叉树
{
    if (subRoot != NULL)
    {
        cout << subRoot->data << " ";
        recursivePreorder(subRoot->left);
        recursivePreorder(subRoot->right);
    }
}

void BinTree::inorder()             //本函数对二叉树进行中序遍历,使用辅助函数recursiveInorder
{
    recursiveInorder(root);
}

void BinTree::recursiveInorder(Bnode *subRoot)//subRoot要么为空,要么指向一棵子二叉树
{
    if (subRoot != NULL)
    {
        recursiveInorder(subRoot->left);
        cout << subRoot->data << " ";
        recursiveInorder(subRoot->right);
    }
}

void BinTree::postorder()           //本函数对二叉树进行后序遍历,使用辅助函数recursivePostorder
{
    recursivePostorder(root);
}

void BinTree::recursivePostorder(Bnode *subRoot)//subRoot要么为空,要么指向一棵子二叉树
{
    if (subRoot != NULL)
    {
        recursivePostorder(subRoot->left);
        recursivePostorder(subRoot->right);
        cout << subRoot->data << " ";
    }
} 

int BinTree::search(const nodeEntry target)const        //在BinTree里查找记录target
{
    Bnode *found = searchNode(root,target);             //调用递归查找函数
    if (found == NULL)
        return OVERFLOW;
    else
        return SUCCESS;
}

Bnode *BinTree::searchNode(Bnode *subRoot, const nodeEntry target)const//递归查找函数,在跟节点为subRoot的查找树里查找target
{
    if (subRoot == NULL || subRoot->data == target)
        return subRoot;
    else if (subRoot->data < target)
        return searchNode(subRoot->right, target);
    else
        return searchNode(subRoot->left, target);
}

/*如果有必要,也可以将递归查找改为循环查找
Bnode *BinTree::searchNode(Bnode *subRoot, const nodeEntry target)const
{
    while (subRoot != NULL&&subRoot->data != target)
        if (subRoot->data < target)
            subRoot = subRoot->right;
        else
            subRoot = subRoot->left;
    return subRoot;
}*/

int BinTree::insert(const nodeEntry newData)//在BinTree里插入记录newData
{
    return searchInsert(root, newData);
}

int BinTree::searchInsert(Bnode * &subRoot, const nodeEntry newData)//递归插入函数
{
    if (subRoot == NULL)
    {
        subRoot = new Bnode;
        if (subRoot == NULL)
        {
            cout << "不能分配新空间,插入失败" << endl;
            return OVERFLOW;
        }
        subRoot->data = newData;
        subRoot->left = NULL;
        subRoot->right = NULL;
    }
    else if (newData < subRoot->data)
        return searchInsert(subRoot->left, newData);
    else if (newData > subRoot->data)
        return searchInsert(subRoot->right, newData);
    else return OVERFLOW;
}

int BinTree::remove(const nodeEntry target) //在BinTree里删除记录target
{
    return searchDelete(root, target);//调用递归查找删除函数
}

int BinTree::searchDelete(Bnode *subRoot, const nodeEntry target)//递归查找函数
{
    if (subRoot == NULL || subRoot->data == target)
        return removeRoot(subRoot);
    else if (target < subRoot->data)
        return searchDelete(subRoot->left, target);
    else 
        return searchDelete(subRoot->right, target);
}

int BinTree::removeRoot(Bnode *subRoot)//辅助函数,在BinTree里删除记录subRoot
{
    if (subRoot == NULL)
        return OVERFLOW;
    Bnode * to_delete = subRoot;
    if (subRoot->right == NULL)         //右子树为空,往左面查找
        subRoot = subRoot->left;
    else if(subRoot->left == NULL)      //左子树为空,往右面查找
        subRoot = subRoot->right;
    else                                //两棵子树皆不为空
    {
        to_delete = subRoot->left;      //往左寻找节点subRoot的前驱节点
        Bnode *parent = subRoot;        //parent记录节点to_delete的父节点
        while (to_delete->right != NULL)
        {
            parent = to_delete;         //to_delete不是前驱节点
            to_delete = to_delete->right;
        }
        subRoot->data = to_delete->data;//从节点to_delete移动到根节点
        if (parent == subRoot)
            subRoot->left = to_delete->left;
        else
            parent->right = to_delete->left;
    }
    delete to_delete;
    return SUCCESS;
}

bool BinTree::empty()const  //判断二叉树是否为空
{
    return root == NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值