数据结构与算法分析张琨PDF_数据结构与算法分析4.3

最近读《数据结构与算法分析》的二叉树这一部分,实在是烧脑筋。感觉比双向链表还要抽象。勉强算弄懂了4.3中的查找,插入,和删除操作。这里作者的代码运用了大量的递归,当然,也有很多的技巧,学到了不少东西。

首先先要明确这样一个事实,即:对于树中的每个节点x,它的左子树的所有项的值都小于x中的项,而右子树的所有项的值都大于x中的项。

9d2ac2202592c411a39ac6c9e6c9d3a2.png

好,现在给出了一个int类型的二叉查找树。当然,用C++的模板的话也是很简单的。这个接口代码如下:

#include<iostream>

using namespace std;

class BinarySearchTrees {

public:

BinarySearchTrees();

BinarySearchTrees(const BinarySearchTrees &rhs);

BinarySearchTrees(BinarySearchTrees&& rhs);

~BinarySearchTrees();

const int& findMin()const {};

const int& findMax()const;

bool contains(const int& x)const;

bool isEmpty()const;

void printTree(ostream& out = cout)const;

void makeEmpty();

void insert(const int& x);

void insert(int&& x);

void remove(const int& x);

BinarySearchTrees& operator=(const BinarySearchTrees& rhs);

BinarySearchTrees& operator=(BinarySearchTrees&& rhs);

private:

struct BinaryNode {

int element;

BinaryNode* left;

BinaryNode* right;

BinaryNode(const int& theElement, BinaryNode* lt, BinaryNode* rt) :

left{ lt }, right{ rt }, element{theElement}{};

BinaryNode(int&& theElement, BinaryNode* lt, BinaryNode* rt) :

element{ std::move(theElement) }, left{ lt }, right{ rt }{};

};

BinaryNode* root;

void insert(const int& x, BinaryNode*& t);

void insert(int&& x, BinaryNode*& t);

void remove(const int&x, BinaryNode*& t);

BinaryNode* FindMax(BinaryNode* t)const;

BinaryNode* FindMin(BinaryNode* t)const;

bool contains(const int& x, BinaryNode* t)const;

void makeEmpty(BinaryNode*& t);

void printTree(BinaryNode* t, ostream& out)const;

BinaryNode* clone(BinaryNode* t)const;

};

其中public部分中的某些函数是为了方便调用private部分的函数,主要目的就是封装。

而第一个操作是查找某个数是否在这个二叉查找树里面。

根据这个二叉查找树的性质,它的左子树的所有项的值都小于x中的项;右子树的所有的项的值都大于x中的项。那么,仅仅需要分别运用递归的手法,按照这个性质即可查找。

bool BinarySearchTrees::contains(const int& x, BinaryNode* t)const {

if (t == nullptr)

return false; //如果一直查找到尽头还是没有,那么返回flase;

else if (x < t->element)

return contains(x, t->left);

//如果查找的项x<当前节点的值,那么向这个节点的左子树去查找;

else if (x > t->element)

return contains(x, t->right);

//如果查找的项x>当前节点的值,那么向这个节点的左子树去查找;

else

return true;//除了这三种情况以外,自然是找到了;

}

接下来的操作是寻找树的最小值和树的最大值。这个操作也用这个性质来实现。

查找最小就一路从左子树出发,当当前节点没有左节点的时候,那显然它是最小的。

BinarySearchTrees::BinaryNode* BinarySearchTrees::FindMin(BinaryNode* t)const {

if (t == nullptr)

return nullptr;

if (t->left == nullptr)

return t;

return FindMin(t->left);

}

右子树用来查找最大

BinarySearchTrees::BinaryNode* BinarySearchTrees::FindMax(BinaryNode* t)const {

if (t == nullptr)

return nullptr;

if (t->right == nullptr)

return t;

return FindMax(t->right);

}

同时,注意到这里的递归是用尾递归的形式来实现的。尾递归可以用while来转化。如下代码所示。

FindMin(BinaryNode*t)const{

if(t!=nullptr)

while(t->left!=nullptr)

t=t->left;

return t;

}

接下来是插入操作。这个插入操作思路也很简单。如果找到x,那么什么也不做;否则,将x插入到遍历路径的最后一点上。而这个操作的实现,又要依靠之前遍历的方法了。除此之外,指针本身也要产生改变。因此,在这里用到了一个技巧,int *&t,这代表这个int指针既可以改变指针指向,也可以改变自己本身。

代码如下:

void BinarySearchTrees::insert(const int& x, BinaryNode*& t) {

if (t == nullptr)

t = new BinaryNode{ x,nullptr,nullptr };

else if (t->element > x)

insert(x, t->right);

else if (t->element < x)

insert(x, t->left);

else

;

}

void BinarySearchTrees::insert(int&& x, BinaryNode*& t) {

if (t == nullptr)

t = new BinaryNode{ std::move(x),nullptr,nullptr };

else if (t->element > x)

insert(std::move(x), t->right);

else if (t->element < x)

insert(std::move(x), t->right);

else

;

}

在递归例程中,只有当一个新树叶生成的时候,t才改变。当这种情况发生的时候,就说明递归例程被其它节点调用了。该节点p是新树叶的父亲。它的调用者将是insert(x,p->left)或者是insert(x,p->right)。换句话说,当递归调用到最后,达到条件时,那一瞬间,t产生了变化,而原先的调用函数的本来是t的子节点的节点变成了t的父亲。当然,这是我个人的想法。也许情况并不是这样。

接下来,是比较麻烦的删除。删除操作相比较起来其实是最难的。如果节点仅仅只是一片树叶,那么直接删除就好了。如果节点有一个儿子,那么删去节点,改变指向就好了。真正麻烦的是一个节点有两个子节点。这时候又要运用性质了。只需要用右子树的最小的数据代替这片要被删除的节点就好了。如图。

325f04808b42184087369646c6a200dc.png

void BinarySearchTrees::remove(const int& x, BinaryNode*& t) {

if (t == nullptr)

return;

else if (t->element < x)

remove(x, t->left);

else if (t->element > x)

remove(x, t->right);

//这一步是遍历的步骤。去寻找要去删除的元素

//如果找到了这个元素,但是却是有两个子节点的树,那么就要分两步处理了

第一步:找到右子树的最小值来代替。

第二部:删除找到的那个右子树的最小值

else if (t->left != nullptr && t->right != nullptr) {

t->element = FindMin(t->right)->element;//第一步的实现

remove(t->element, t->right);//第二步的删除

}

else {//接下来就是针对单子节点和无子节点的删除操作了。

BinaryNode* old = t;

t = (t->left != nullptr) ? t->left : t->right;

delete old;

}

}

最后,就是整个类的析构了。这个也不是太难。还是递归调用。

BinarySearchTrees::~BinarySearchTrees() {

makeEmpty();

}

void BinarySearchTrees::makeEmpty(BinaryNode*&t) {

if (t != nullptr) {

makeEmpty(t->left);

makeEmpty(t->right);

delete t;

}

t = nullptr;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值