【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)平衡与等价
随机推荐
- 如果你的unordered_map头文件报错请看这里
请将include<unordered_map>头文件换成下面代码 #if(__cplusplus == 201103L) #include <unordered_map> # ...
- 在腾讯云服务器上安装JDK+Tomcat并启动tomcat
由于Java web项目需要使用到tomcat所以决定在腾讯云服务器(centos7.X)上安装JDK和tomcat,来部署我们的项目. 准备工具: 云服务器:centos7.x+ 本地连接服务器:X ...
- Ubuntu上搭建GPU服务器
1.安装显卡驱动 2.安装CUDA 3.安装cuDNN 下载: 根据显卡类型以及操作系统,选定CUDA版本和语言设置,下载对应的显卡驱动. 驱动下载地址 安装 $ sudo ./NVIDIA-Linu ...
- python——实现三级菜单选择的功能(原创)
#coding:utf-8 dict={'beijing':{'haidingqu':['qinghe','keji'],'chaoyangqu':['q','w']},'shandong':{'li ...
- Python如何让字典保持有序
问题: Python如何让字典保持有序 ? 解决方案: 使用collections.OrderedDict代替Dict. 验证程序: from collections import OrderedDi ...
- 什么是SNAT
SNAT是源地址转换,其作用是将ip数据包的源地址转换成另外一个地址,可能有人觉得奇怪,好好的为什么要进行ip地址转换啊,为了弄懂这个问题,我们要看一下局域网用户上公网的原理,假设内网主机A(192. ...
- 通过pl/sql连接远程Oracle数据库
通过PL/SQL连接远程数据库,简单的方式就是安装Oracle客户端,还有一种方式就是不安装客户端,但是需要自己创建必要的配置文件,下面主要对安装客户端的过程简单做一下记录. 网上一个不安装客户端的教 ...
- percona-toolkit 之 【pt-query-digest】使用
背景 关于pt-query-digest的使用场景和方法在percona-toolkit 之 [pt-query-digest]介绍文章里已经做了详细说明,现在开始介绍下如何使用,以及常用的命令. 使 ...
- LeetCode--链表2-双指针问题
LeetCode--链表2-双指针问题 思考问题: 判断一个链表是否有环 列举几种情况: graph LR A-->B B-->C C-->D D-->E E-->C g ...
- 深度解析互联网大厂面试难题自定义@EnableXX系列
深度解析互联网大厂面试难题自定义@EnableXX系列 其实是一个@Import的设计技巧 创建注解@EnableXX(任何名称注解都行,只是这个名字好一些) XXConfiguration类不能 ...