设计思路

设计一个类,根结点只可读取,具备构造二叉树、插入结点、删除结点、查找、 查找最大值、查找最小值、查找指定结点的前驱和后继等功能接口。

二叉排序树概念

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

二叉排序树的各种操作

插入新节点
这是一个递归操作,递归设计时要找到最源头,才能得到最简设计。一种设计是判断叶子节点,把新节点作为叶子节点的孩子插入;一种是永远当作根进行插入,插入节点永远是当前子树的根!看代码:

//root为二级指针的原因是,如果树为空,需要将根修改反馈回来
bool BinaryTree::InsertNode(pNode * cuRoot, int data, pNode self)
{ //递归设计时找到最源头,才能得到最简设计
if (*cuRoot == nullptr){
pNode node = new Node;
if (node == nullptr)
return false;
node->data = data;
node->lChild = node->rChild = node->parent = nullptr;
(*cuRoot) = node;
node->parent = self;
return true;
}
if (data > (*cuRoot)->data)
InsertNode(&(*cuRoot)->rChild, data, *cuRoot);
else
InsertNode(&(*cuRoot)->lChild, data, *cuRoot);
return true;
}

构造函数
一共两个重载函数:一个无参,一个接受数组利用插入函数直接构造二叉排序树。

BinaryTree::BinaryTree(int * datum, int len)
{
root = nullptr;
for (int i = ; i < len; i++)
InsertNode(&root, datum[i], root);
} BinaryTree::BinaryTree()
{
root = nullptr;
}

查找函数
这也是一个递归操作,为了对外隐藏root(根节点),因此编写了一个私有函数,进行真正的查找操作。

//真正的查找函数
BinaryTree::pNode BinaryTree::_searchKey(pNode root, int key){
if (root == nullptr)
return nullptr;
if (root->data == key) //找到了
return root;
else if (root->data > key)//值偏小,到左子树找
return _searchKey(root->lChild, key);
else //值偏大,到右子树找
return _searchKey(root->rChild, key);
}
//对外接口
BinaryTree::pNode BinaryTree::SearchKey(int key){
return _searchKey(root, key);
}

找前驱节点
要么为左子树中最大者,要么一直追溯其父节点链,第一个是其父节点的右孩子的父节点,即为所求。

BinaryTree::pNode BinaryTree::SearchPredecessor(pNode node){
if (node == nullptr)
return nullptr;
else if (node->lChild != nullptr)
return SearchMaxNode(node->lChild);
else
{
if (node->parent == nullptr)
return nullptr;
while (node)
{
if (node->parent->rChild == node)
break;
node = node->parent;
}
return node->parent;
}
}

找后继节点
与找前驱节点基本相似。 要么为右子树中最小者,要么一直追溯其父节点链,第一个是其父节点的左孩子的父节点,即为所求。

BinaryTree::pNode BinaryTree::SearchSuccessor(pNode node){
if (node == nullptr)
return nullptr;
else if (node->rChild != nullptr)
return SearchMinNode(node->rChild);
else
{
if (node->parent == nullptr)
return nullptr;
while (node)
{
if (node->parent->lChild == node)
break;
node = node->parent;
}
return node->parent;
}
}

找最小值

BinaryTree::pNode BinaryTree::SearchMinNode(pNode curNode){
if (curNode == nullptr)
return nullptr;
//一直找到左子树为空的节点,即为最小值
while (curNode->lChild != nullptr)
curNode = curNode->lChild;
return curNode;
}

找最大值

BinaryTree::pNode BinaryTree::SearchMaxNode(pNode curNode){
if (curNode == nullptr)
return nullptr;
//一直找到右子树为空的节点,即为最大值
while (curNode->rChild != nullptr)
curNode = curNode->rChild;
return curNode;
}

中序遍历

void BinaryTree::_visitMiddle(pNode root){
if (root != nullptr){
_visitMiddle(root->lChild);
printf("%d;", root->data);
_visitMiddle(root->rChild);
}
} void BinaryTree::VisitMiddle(){
_visitMiddle(root);
}

删除节点
这个是最麻烦的操作,分四种情况分别处理,最麻烦的是被删节点左右子树都存在的情况,这时将被删节点内容换成其后继内容,删除其后继(递归)。

bool BinaryTree::DeleteNode(int key){
//return _deleteNode(root, key);
pNode node = SearchKey(key);
if (!node)
return false;
//被删节点为叶子结点
if (node->lChild == nullptr && node->rChild == nullptr){
if (node->parent == nullptr){
root = nullptr;
}
else
{
if (node->parent->lChild == node)
node->parent->lChild = nullptr;
else
node->parent->rChild = nullptr;
}
delete node;
}
//被删节点只有左子树
else if (node->lChild != nullptr && node->rChild == nullptr){
//将左孩子的父亲指向被删节点的父亲
node->lChild->parent = node->parent;
//被删节点为根,修改根节点
if (node->parent == nullptr)
root = node->lChild;
else if(node->parent->lChild == node)
node->parent->lChild = node->lChild;
else
node->parent->rChild = node->lChild;
delete node;
}
//被删节点只有右子树
else if (node->lChild == nullptr && node->rChild != nullptr){
//将右孩子的父亲指向被删节点的父亲
node->rChild->parent = node->parent;
//被删节点为根,修改根节点
if (node->parent == nullptr)
root = node->rChild;
else if (node->parent->lChild == node)
node->parent->lChild = node->rChild;
else
node->parent->rChild = node->rChild;
delete node;
}
//被删节点左、右子树都有
else { //用后继节点取代删除节点,并删除后继
pNode successor = SearchSuccessor(node);
int temp = successor->data;
DeleteNode(temp);
node->data = temp;
}
}

柝构函数
函数超出作用域范围时,清理占用内存。

BinaryTree::~BinaryTree()
{
_delAllNode(root);
}
void BinaryTree::_delAllNode(pNode root){
if (root != nullptr && root!=NULL){
_delAllNode(root->lChild);
_delAllNode(root->rChild);
DeleteNode(root->data);
}
}

类的定义(头文件)

#pragma once

#include<stdio.h>
#include<stdlib.h> class BinaryTree
{
private:
typedef struct Node{
struct Node * parent;
struct Node * lChild;
struct Node * rChild;
int data;
}*pNode;
pNode root;
void _visitMiddle(pNode root);
pNode _searchKey(pNode root, int key);
void _delAllNode(pNode root);
public:
BinaryTree();
BinaryTree(int * datum, int len);
pNode SearchMaxNode(pNode node);
pNode SearchMinNode(pNode node);
pNode GetRoot();
pNode SearchKey(int key);
bool DeleteNode(int key);
pNode SearchPredecessor(pNode node);
pNode SearchSuccessor(pNode node);
void VisitMiddle();
bool InsertNode(pNode * cuRoot, int data, pNode self);
~BinaryTree();
};

调用示例

#include <conio.h>
#include "BinaryTree.h" int main()
{
int arrs[] = { , , , , , , , , , ,, };
int len = sizeof(arrs) / sizeof(arrs[]);
BinaryTree bTree(arrs,len);
bTree.DeleteNode();
bTree.VisitMiddle();
getch();
return ;
}

二叉排序树实现(C++封装)的更多相关文章

  1. POJ 2418 各种二叉排序树

    题意很明确,统计各个字符串所占总串数的百分比,暴力的话肯定超时,看了书上的题解后发现这题主要是用二叉排序树来做,下面附上n种树的代码. 简单的二叉排序树,不作任何优化(C语言版的): #include ...

  2. 算法与数据结构(十) 二叉排序树的查找、插入与删除(Swift版)

    在上一篇博客中,我们主要介绍了四种查找的方法,包括顺序查找.折半查找.插入查找以及Fibonacci查找.上面这几种查找方式都是基于线性表的查找方式,今天博客中我们来介绍一下基于二叉树结构的查找,也就 ...

  3. [C#] 简单的 Helper 封装 -- RegularExpressionHelper

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  4. iOS开发之App间账号共享与SDK封装

    上篇博客<iOS逆向工程之KeyChain与Snoop-it>中已经提到了,App间的数据共享可以使用KeyChian来实现.本篇博客就实战一下呢.开门见山,本篇博客会封装一个登录用的SD ...

  5. Ajax实现原理,代码封装

    都知道实现页面的异步操作需要使用Ajax,那么Ajax到是怎么实现异步操作的呢? 首先需要认识一个对象 --> XMLHttpRequest 对象 --> Ajax的核心.它有许多的属性和 ...

  6. 用C语言封装OC对象(耐心阅读,非常重要)

    用C语言封装OC对象(耐心阅读,非常重要) 本文的主要内容来自这里 前言 做iOS开发的朋友,对OC肯定非常了解,那么大家有没有想过OC中NSInteger,NSObject,NSString这些对象 ...

  7. 【知识必备】RxJava+Retrofit二次封装最佳结合体验,打造懒人封装框架~

    一.写在前面 相信各位看官对retrofit和rxjava已经耳熟能详了,最近一直在学习retrofit+rxjava的各种封装姿势,也结合自己的理解,一步一步的做起来. 骚年,如果你还没有掌握ret ...

  8. 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传)

    前言 首先声明一下,我这个是对WebUploader开源上传控件的二次封装,底层还是WebUploader实现的,只是为了更简洁的使用他而已. 下面先介绍一下WebUploader 简介: WebUp ...

  9. 封装集合(Encapsulate Collection)

    封装就是将相关的方法或者属性抽象成为一个对象. 封装的意义: 对外隐藏内部实现,接口不变,内部实现自由修改. 只返回需要的数据和方法. 提供一种方式防止数据被修改. 更好的代码复用. 当一个类的属性类 ...

随机推荐

  1. 编写优质嵌入式C程序(转)

    前言:这是一年前我为公司内部写的一个文档,旨在向年轻的嵌入式软件工程师们介绍如何在裸机环境下编写优质嵌入式C程序.感觉是有一定的参考价值,所以拿出来分享,抛砖引玉. 转载请注明出处:http://bl ...

  2. laravel5.5 不能正常自动回复的问题

    虽然开启了APP_DEBUG 但是 log 却没有记录任何错误信息,后来经过测试发现原来是路由问题,因为微信服务器发送消息是使用 post 方法,但是我的路由定义只定义了 get (tp 用多了习惯了 ...

  3. Qt ------ 判断运行在何种系统下

    #ifdef Q_OS_WIN // Windows上的代码 #endif #ifdef Q_OS_LINUX // Linux上的代码 #endif #ifdef Q_OS_MAC // Mac上的 ...

  4. crontab 自动执行脚本

    crontab -e ================>自动执行某脚本!!!!!!! 1001 ls 1002 cd /home/wwwroot/default/ 1003 ls 1004 cr ...

  5. 第1章-初识Vue.js

    一.初识Vue 1.1.本次我们学习的内容 常用指令:vue中最基础的内容 交互: 网络请求 组件: 是vue.js 这个框架 最核心,最精华的内容,因为vue呐,它在所有的框架中是把组件化做到了极致 ...

  6. MySQL下创建序列及创建自定义函数方法介绍

    工作过程中需要将基于DB2数据库的应用以及数据迁移到MySQL中去,在原应用中,大量使用了SEQUENCE,考虑尽量减少代码的修改,决定在迁移后的应用中继续保留SEQUENCE的使用,这就要求在MyS ...

  7. Java项目中读取properties文件,以及六种获取路径的方法

    下面1-4的内容是网上收集的相关知识,总结来说,就是如下几个知识点: 最常用读取properties文件的方法 InputStream in = getClass().getResourceAsStr ...

  8. Linux下如何卸载软件(Debian系)

    说明:此方法适用于Debian.Ubuntu等带apt工具的操作系统. 步骤: 1.首先我们需要知道将要卸载的软件名称,比如我现在打算卸载tightvncserver,但是如果你不确定名称,没关系,可 ...

  9. Spark RDD——combineByKey

    为什么单独讲解combineByKey? 因为combineByKey是Spark中一个比较核心的高级函数,其他一些高阶键值对函数底层都是用它实现的.诸如 groupByKey,reduceByKey ...

  10. 从零搭建SSM框架(二)运行工程

    启动cnki-manager工程 1.需要在cnki-manager 的pom工程中,配置tomcat插件.启动的端口号,和工程名称. 在cnki-manager的pom文件中添加如下配置: < ...