【Weiss】【第04章】二叉搜索树例程
【二叉搜索树】
随机生成时平均深度为logN,平均插入、删除和搜索时间都是O(logN)。
可能存在的问题是数据不均衡,使树单边生长,极端情况下变成类似链表,最坏插入、删除、搜索时间O(N)
写这个例程也花了些时间,例程主要包括查找、插入、删除、遍历、清空几个内容。
同时避免之前写链表到了半途再修改,拷贝构造、拷贝赋值、析构也写成了深拷贝的模式。
光这两份代码在一起还无法运行,因为层序遍历那一块用了之前写的队列例程。
测试代码:
#include <iostream>
#include "bstree.h"
using bstree::BSTree;
int main(void)
{
//测试插入
BSTree<int> number;
cout << "additem" << endl;
number.additem();number.additem();number.additem();number.additem();
number.additem();number.additem();number.additem();number.additem();
number.additem();number.additem();number.additem();number.additem();
number.additem();number.additem();number.additem();number.additem();
number.additem(); number.additem(); number.additem(); number.additem();
//打印
number.printpre();
cout << "--preorder"<<endl;
number.printin();
cout << "--inorder" << endl;
number.printpost();
cout << "--postorder" << endl;
number.printseq();
cout << "--seqorder" << endl; //测试拷贝构造函数
cout << "\ncopy constructor" << endl;
BSTree<int> number2(number);
number2.printpre();
cout << "--preorder" << endl;
number2.printin();
cout << "--inorder" << endl; //测试插入重复元素
cout << "\ncollision" << endl;
number.additem(); //删除测试
//删除节点没有右子树
cout << "\nremove number 3: iter->right = nullptr" << endl;
number.remove();
number.printpre();
cout << "--preorder" << endl;
number.printin();
cout << "--inorder" << endl; //删除测试
//删除节点的右子树没有左子树
cout << "\nremove number 15: iter->right->left = nullptr" << endl;
number.remove();
number.printpre();
cout << "--preorder" << endl;
number.printin();
cout << "--inorder" << endl; //删除测试
//删除节点的右子树有左子树
cout << "\nremove number 10: iter->right->left != nullptr" << endl;
number.remove();
number.printpre();
cout << "--preorder" << endl;
number.printin();
cout << "--inorder" << endl; //删除测试
//删除根
cout << "\nremove number 8: iter == root" << endl;
number.remove();
number.printpre();
cout << "--preorder" << endl;
number.printin();
cout << "--inorder" << endl; //删除测试
//删除不存在的元素
cout << "\nremove number not existed" << endl;
number.remove(); //查找最值
cout << "\nmax = " << number.findmax()->data << endl;
cout << "min = " << number.findmin()->data << endl; //清空树,并测试在空树下删除
cout << "\nclear" << endl;
number.clear();
number.remove(); system("pause");
}
实现代码:
#ifndef BINARYSEARCHTREE
#define BINARYSEARCHTREE
#include <iostream>
#include "queue.h"
using queue::Queue;
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::flush;
namespace bstree
{
template <typename T> struct Node
{
Node<T>() : left(nullptr), right(nullptr){}
Node<T>(const T &item) : data(item), left(nullptr), right(nullptr){}
T data;
Node<T>* left;
Node<T>* right;
};
template <typename T> class BSTree
{
//构造函数
public:
BSTree() :nodes(), root(nullptr){}
BSTree(const BSTree& another)
{
root = nullptr;
nodes = ;
void(BSTree::*pf)(const Node<T>*);
pf = &BSTree::additem_from_another;
traverse_seq(another.root, pf);
}
~BSTree(){ clear(); }
BSTree& operator=(const BSTree& another)
{
if (&another != this)
{
clear();
void(BSTree::*pf)(const Node<T>*);
pf = &BSTree::additem_from_another;
traverse_seq(another.root, pf);
}
}
//接口
public:
//元素个数
unsigned int size()const{ return nodes; }
//是否空
bool empty()const{ return noodes == ; }
//获得根指针
Node<T>* getroot()const{ return root; }
//各种遍历
//前中后序使用辅助函数递归完成,层序遍历需使用队列结构
void printpre()const{ traverse(root, printnode, ); }
void printin()const{ traverse(root, printnode, ); }
void printpost()const{ traverse(root, printnode, ); }
void printseq()const{ traverse_seq(root, printnode); }
//#查找元素
Node<T>* find(const T &item)const;
//#查找最值
Node<T>* findmax()const;
Node<T>* findmin()const;
//#增加元素
bool additem(const T &item);
//#删除元素
bool remove(const T &item);
//清空树并将数据初始化
void clear(){ release(root); root = nullptr; nodes = ; } //辅助函数
private:
void release(Node<T>* iter);
Node<T>* find_prev(const T &item)const;
Node<T>* remove_node(Node<T>* iter);
void traverse(const Node<T>* iter, void(*fp)(const Node<T>*), int mode)const;
void traverse_seq(const Node<T>* iter, void(BSTree::*pf)(const Node<T>*));
void traverse_seq(const Node<T>* iter, void(*pf)(const Node<T>*))const;
void additem_from_another(const Node<T>* iter){ additem(iter->data); }
//数据
private:
Node<T>* root;
unsigned int nodes;
}; //辅助函数,打印节点元素
template <typename T> static void printnode(const Node<T>* iter){ cout << iter->data << " " << flush; }
//辅助函数
//查找元素的父树。如果树为空或元素为根,返回空指针;如果元素不存在,返回插入该元素时的父树
template <typename T> Node<T>* BSTree<T>::find_prev(const T &item)const
{
Node<T>* prev = nullptr;
Node<T>* curr = root;
while (curr != nullptr)
{
if (item < curr->data)
{
prev = curr;
curr = curr->left;
}
else if (item > curr->data)
{
prev = curr;
curr = curr->right;
}
else
break;
}
return prev;
}
//查找元素,如果元素不存在则返回空指针
template <typename T> Node<T>* BSTree<T>::find(const T &item)const
{
Node<T>* iter = find_prev(item);
if (iter == nullptr)
return root;
if (item < iter->data)
return iter->left;
else
return iter->right;
}
//查找最值
template <typename T> Node<T>* BSTree<T>::findmax()const
{
if (nodes == )
return nullptr;
Node<T>* iter = root;
while (iter->right != nullptr)
iter = iter->right;
return iter;
}
template <typename T> Node<T>* BSTree<T>::findmin()const
{
if (nodes == )
return nullptr;
Node<T>* iter = root;
while (iter->left != nullptr)
iter = iter->left;
return iter;
}
//插入元素,元素重复则报错
template <typename T> bool BSTree<T>::additem(const T &item)
{
Node<T>* prev = nullptr;
Node<T>* curr = root;
//找到待插入元素应该插入的位置
while (curr != nullptr)
{
if (item < curr->data)
{
prev = curr;
curr = curr->left;
}
else if (item > curr->data)
{
prev = curr;
curr = curr->right;
}
//元素相等导致冲突则报错
else
{
cout << "Elements collision!" << endl;
return false;
}
}
Node<T>* pnew = new Node<T>(item);
if (prev == nullptr)
root = pnew;
else if (item < prev->data)
prev->left = pnew;
else
prev->right = pnew;
++nodes;
return true; }
//删除元素
template <typename T> bool BSTree<T>::remove(const T &item)
{
if (nodes == )
{
cout << "Tree is empty!" << endl;
return false;
}
//函数主体执行查找操作
//查找到相应删除点后,交给remove_node()处理
Node<T>* iter = find_prev(item);
if (iter == nullptr)
{
if (root->data != item)
{
cout << "Not Found!" << endl;
return false;
}
root = remove_node(root);
}
else if (item < iter->data)
{
if (iter->left == nullptr)
{
cout << " Not Found!" << endl;
return false;
}
iter->left = remove_node(iter->left);
}
else
{
if (iter->right == nullptr)
{
cout << "Not Found!" << endl;
return false;
}
iter->right = remove_node(iter->right);
}
--nodes;
return true;
}
//辅助函数,删除具体节点
template <typename T> Node<T>* BSTree<T>::remove_node(Node<T>* iter)
{
//用即将被删除节点的右子树的最左边的子节点替代即将被删除的位置
//情况一,删除节点无右子树
if (iter->right == nullptr)
{
Node<T>* save = iter->left;
delete iter;
return save;
}
//情况二,删除节点的右子书无左子树
else if (iter->right->left == nullptr)
{
Node<T>* save = iter->right;
save->left = iter->left;
delete iter;
return save;
}
//情况三,删除节点的右子树有左子树
else
{
Node<T>* prev = iter->right;
Node<T>* curr = iter->right->left;
while (curr->left != nullptr)
{
prev = curr;
curr = curr->left;
}
prev->left = curr->right;
curr->left = iter->left;
curr->right = iter->right;
delete iter;
return curr;
}
}
//辅助函数,前中后遍历执行模式
template <typename T> void BSTree<T>::traverse(const Node<T>* iter, void(*fp)(const Node<T>*), int mode)const
{
if (iter == nullptr)
return;
if (mode == )
{
(*fp)(iter);
traverse(iter->left, fp, );
traverse(iter->right, fp, );
}
else if (mode == )
{
traverse(iter->left, fp, );
(*fp)(iter);
traverse(iter->right, fp, );
}
else if (mode == )
{
traverse(iter->left, fp, );
traverse(iter->right, fp, );
(*fp)(iter);
}
return;
}
//辅助函数,层序遍历,需队列辅助
//重载两个版本,一个用于遍历树不需改变树元素,调用非成员函数printnode。则标记const,函数参数为(*pf)
//另一个用于拷贝构造与拷贝赋值需改变树元素,调用成员函数。则不标记const,显示声明调用成员函数(BSTree::*pf)
template <typename T> void BSTree<T>::traverse_seq(const Node<T>* iter, void(BSTree::*pf)(const Node<T>*))
{
if (iter != nullptr)
{
Queue<const decltype(iter)> node_address;
node_address.enqueue(iter);
while (!node_address.empty())
{
const Node<T>* temp = node_address.getfirst();
node_address.dequeue();
if (temp->left != nullptr)
node_address.enqueue(temp->left);
if (temp->right != nullptr)
node_address.enqueue(temp->right);
(this->*pf)(temp);
}
}
}
template <typename T> void BSTree<T>::traverse_seq(const Node<T>* iter, void(*pf)(const Node<T>*))const
{
if (iter != nullptr)
{
Queue<const decltype(iter)> node_address;
node_address.enqueue(iter);
while (!node_address.empty())
{
const Node<T>* temp = node_address.getfirst();
node_address.dequeue();
if (temp->left != nullptr)
node_address.enqueue(temp->left);
if (temp->right != nullptr)
node_address.enqueue(temp->right);
(*pf)(temp);
}
}
}
//辅助函数,释放所有节点清空内存
template <typename T> void BSTree<T>::release(Node<T>* iter)
{
if (iter == nullptr)
return;
release(iter->left);
release(iter->right);
delete(iter);
}
}
#endif
【Weiss】【第04章】二叉搜索树例程的更多相关文章
- 算法进阶面试题04——平衡二叉搜索树、AVL/红黑/SB树、删除和调整平衡的方法、输出大楼轮廓、累加和等于num的最长数组、滴滴Xor
接着第三课的内容和讲了第四课的部分内容 1.介绍二叉搜索树 在二叉树上,何为一个节点的后继节点? 何为搜索二叉树? 如何实现搜索二叉树的查找?插入?删除? 二叉树的概念上衍生出的. 任何一个节点,左比 ...
- 算法导论(Introduction to Algorithms )— 第十二章 二叉搜索树— 12.1 什么是二叉搜索树
搜索树数据结构支持很多动态集合操作,如search(查找).minmum(最小元素).maxmum(最大元素).predecessor(前驱).successor(后继).insert(插入).del ...
- 算法导论 第十二章 二叉搜索树(python)
上图: 这是二叉搜索树(也有说是查找树的)基本结构:如果y是x的左子树中的一个结点,那么y.key <= x.key(如a图中的6根结点大于它左子树的每一个结点 6 >= {2,5,5}) ...
- 第七章 二叉搜索树(d4)AVL树:(3+4)-重构
- 第七章 二叉搜索树 (d3)AVL树:删除
- 第七章 二叉搜索树(b3)BST:删除
- 第七章 二叉搜索树 (d2)AVL树:插入
- 第七章 二叉搜索树 (d1)AVL树:重平衡
- 第七章 二叉搜索树(c)平衡与等价
随机推荐
- python3下scrapy爬虫(第一卷:安装问题)
一般爬虫都是用urllib包,requests包 配合正则.beautifulsoup等包混合使用,达到爬虫效果,不过有框架谁还用原生啊,现在我们来谈谈SCRAPY框架爬虫, 现在python3的兼容 ...
- Nginx笔记总结七:root和alias文件路径配置
1. root path 配置段:http.server.location.if location ~ ^/weblogs/ { root /data/weglogs/www.ttlsa.com; a ...
- js弱类型转换的知识点
本文属于转载知识点,以下是原博文作者:不死鸟哇的文章,文章链接:原文JavaScript里什么情况下a==!a为true呢? 今天群里有位同学问了这样一个问题,JavaScript在什么情况下会出现变 ...
- Jekyll的_config文件配置报错
8 April 2019 by IchiehPan Jekyll 搭建博客时, 修改完_config.yml文件后就收到了github的build error邮件. 大致内容如下: did not f ...
- iOS多线程开发之GCD(中级篇)
前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...
- SpringBoot(七)-SpringBoot JPA-Hibernate
步骤 1.在pom.xml添加mysql,spring-data-jpa依赖2.在application.properties文件中配置mysql连接配置文件3.在application.proper ...
- springboot+jwt实现token登陆权限认证
一 前言 此篇文章的内容也是学习不久,终于到周末有时间码一篇文章分享知识追寻者的粉丝们,学完本篇文章,读者将对token类的登陆认证流程有个全面的了解,可以动态搭建自己的登陆认证过程:对小项目而已是个 ...
- tomcat服务器的应用总结
tomcat的安装和部署: >> Web的基本入门: |-- C/S架构:客户端和服务器: |-- B/S架构:浏览器和服务器: >> 服务器当中可以放入的资源: |-- 静态 ...
- Asp.Net Core EndPoint 终点路由工作原理解读
一.背景 在本打算写一篇关于Identityserver4 的文章时候,确发现自己对EndPoint -终结点路由还不是很了解,故暂时先放弃了IdentityServer4 的研究和编写:所以才产生了 ...
- php-fpm启动失败处理
报错信息: No pool defined. at least one pool section must be specified in config file [11-Mar-2019 23:57 ...