AVL树C++实现(插入,删除,查找,清空,遍历操作)
AVL.h文件代码
#pragma once
#include<iostream>
#include<stack>
#include <assert.h>
using namespace std;
using namespace std;
template<class T>
struct AVLNode{
T data;
AVLNode<T>*left, *right;
int bf;
AVLNode() :left(NULL), right(NULL), bf(0) {}
AVLNode(T d, AVLNode<T>*l=NULL,AVLNode<T>*r=NULL):data(d),left(l),right(r),bf(0){}
};
template<class T>
class AVLTree {
public:
AVLTree():root(NULL){}
~AVLTree() {};
AVLTree(T Ref):Refvalue(Ref),root(NULL){}
bool insert(AVLNode<T>*&ptr, T&e1);
bool Remove(AVLNode<T>*&ptr, T x);
void RotateL(AVLNode<T>*&ptr);
void RotateR(AVLNode<T>*&ptr);
void PrintBinTree(AVLNode<T>*&t, int level);
void RotateLR(AVLNode<T>*&ptr);
void RotateRL(AVLNode<T>*&ptr);
AVLNode<T>* search(const T x, AVLNode<T>*ptr);
friend istream& operator>>(istream&, AVLTree<T>& Tree);
friend ostream& operator<<(ostream&, AVLTree<T>&Tree);
void Delete(AVLNode<T>*ptr);
void output(AVLNode<T>*ptr, ostream&out);
AVLNode<T>*root;
T Refvalue;
private:
};
template<class T>
void AVLTree<T>::RotateL(AVLNode<T>*&ptr) {
AVLNode<T>*subL = ptr;
ptr = subL->right;
subL->right = ptr->left;
ptr->left = subL;
ptr->bf = subL->bf = 0;
}
template<class T>
void AVLTree<T>::RotateR(AVLNode<T>*&ptr) {
AVLNode<T>*subR = ptr;
ptr = subR->left;
subR->left = ptr->right;
ptr->right = subR;
ptr->bf = subR->bf = 0;
}
template<class T>
void AVLTree<T>::RotateLR(AVLNode<T>*&ptr) {
AVLNode<T>*subR = ptr, *subL = subR->left;
ptr = subL->right;
subL->right = ptr->left;
ptr->left = subL;
if (ptr->bf <= 0)subL->bf = 0;
else subL->bf = -1;
subR->left = ptr->right;
ptr->right = subR;
if (ptr->bf == -1)subR->bf = 1;
else subR->bf = 0;
ptr->bf = 0;
}
template<class T>
void AVLTree<T>::RotateRL(AVLNode<T>*&ptr) {
AVLNode<T>*subL = ptr, *subR = subL->right;
ptr = subR->left;
subR->left = ptr->right;
ptr->right = subR;
if (ptr->bf >= 0)subR->bf = 0;
else subR->bf = 1;
subL->right = ptr->left;
ptr->left = subL;
if (ptr->bf == 1)subL->bf = -1;
else subL->bf = 0;
ptr->bf = 0;
}
template<class T>
bool AVLTree<T>::insert(AVLNode<T>*&ptr, T&e1) {
AVLNode<T>*pr = NULL, *p = ptr, *q; int d;
stack<AVLNode<T>*>st;
while (p!=NULL)
{
if (e1 == p->data) {
cout << "存在,无法插入\n"; return false;
}
pr = p; st.push(pr);
if (e1 < p->data)p = p->left;
else p = p->right;
}
p = new AVLNode<T>(e1);
if (p == NULL) { cout << "内存不足" << endl; exit(1); }
if (pr == NULL) { ptr = p; return true; }//空树,根结点插入
if (e1 < pr->data)pr->left = p;
else pr->right = p;
while (st.empty()==false)
{
pr = st.top();
st.pop();
if (p == pr->left)pr->bf--;
else pr->bf++;
if (pr->bf == 0)break;//case 1,平衡退出
if (abs(pr->bf) == 1) {//case 2
p = pr;//回溯
}
else {//case 3 |bf|==2
d = (pr->bf < 0) ? -1 : 1;
if (p->bf == d) {
if (d == -1)RotateR(pr);
else RotateL(pr);
}
else {
if (d == -1)RotateLR(pr);
else RotateRL(pr);
}
break;
}
}
if (st.empty() == true)ptr = pr;
else {
q = st.top();
if (q->data > pr->data)q->left = pr;
else q->right = pr;
}
return true;
}
//template<class T>
istream& operator >>(istream& in, AVLTree<int>& Tree) {
int item;
in >> item;
while (item!=Tree.Refvalue)
{
Tree.insert(Tree.root,item);
in >> item;
}
return in;
}
//template<class T>
ostream& operator<<( ostream&out, AVLTree<int>&Tree) {
out << "中序遍历输出各个结点数值:" << endl;
Tree.output(Tree.root,out);
out << endl;
return out;
}
template<class T>
void AVLTree<T>::output(AVLNode<T>*ptr, ostream&out) {
if (ptr != NULL) {
output(ptr->left, out);
cout << ptr->data << " ";
output(ptr->right, out);
}
}
template<class T>
AVLNode<T>* AVLTree<T>::search(const T x, AVLNode<T>*ptr) {
if (ptr == NULL)return NULL;
else if (x < ptr->data)return search(x, ptr->left);
else if (x > ptr->data)return search(x, ptr->right);
else return ptr;
}
template<class T>
void AVLTree<T>::Delete(AVLNode<T>*ptr) {
if (ptr != NULL){
Delete(ptr->left);
Delete(ptr->right);
delete ptr;
}
}
template <class T>
void AVLTree<T>::PrintBinTree(AVLNode<T>*&t, int level)
{
if (t == NULL)return;
PrintBinTree(t->right, level + 1);
for (int i = 0; i < 4 * (level - 1); i++)cout << " ";
cout << t->data << endl;
PrintBinTree(t->left, level + 1);
}
template<class T>
bool AVLTree<T>::Remove(AVLNode<T>*&t, T val)
{
assert(t != nullptr);
AVLNode<T> *tmp = t;
AVLNode<T> *pre_tmp = nullptr;
AVLNode<T> *ppre_tmp = nullptr;
AVLNode<T> *it_tmp = nullptr;
stack<AVLNode<T>*> st;
int sign, lable; //符号标记
int flag = 0; //子树标记,下文具体解释
while (tmp != nullptr) {
if (tmp->data == val) //找到跳出循环
break;
pre_tmp = tmp;
st.push(pre_tmp);
if (tmp->data > val)
tmp = tmp->left;
else
tmp = tmp->right;
}
if (tmp == nullptr) //未找到,返回
return false;
else if (tmp->left!= nullptr && tmp->right != nullptr) {
pre_tmp = tmp; //将有两个孩子的节点转化为只有一个孩子的节点,方法是寻找它中序遍历的直接前驱或后继
st.push(pre_tmp);
it_tmp = tmp->left;
while (it_tmp->right!= nullptr) {
pre_tmp = it_tmp;
st.push(pre_tmp);
it_tmp = it_tmp->right;
}
tmp->data = it_tmp->data; //覆盖要删除的节点
tmp = it_tmp; //tmp指向要删除的节点,函数结束前会delete tmp
}
if (tmp->left!= nullptr) { //这样的判断方式会导致删除一个节点下两个没有孩子节点的节点时,由于左孩子均为空,直接跳入else
it_tmp = tmp->left;
}
else {
it_tmp = tmp->right;
}
if (pre_tmp == nullptr)
t = it_tmp;
else {
if (pre_tmp->left== tmp) { //上面直接跳入else,但我们在此处加上flag,用来识别它到底是pre_tmp的左孩子还是右孩子。
flag = 0;
pre_tmp->left= it_tmp;
}
else {
flag = 1;
pre_tmp->right = it_tmp;
}
while (!st.empty()) {
pre_tmp = st.top();
st.pop();
if (pre_tmp->left == it_tmp && flag == 0)//此处flag=0,防止pre_tmp的左孩子为空,右孩子同样为空,直接进入else
pre_tmp->bf++;
else
pre_tmp->bf--;
if (!st.empty())
{
ppre_tmp = st.top();
if (ppre_tmp->left == pre_tmp)
{
sign = -1; //sign用来识别是祖父节点的左孩子还是右孩子,下文链接会用上
flag = 0;
}
else {
sign = 1;
flag = 1;
}
}
else
sign = 0; //栈空,它的祖先节点不存在,pre_tmp即为根节点,但是这里也要写上,否则sign的值会遗留下来
if (pre_tmp->bf == -1 || pre_tmp->bf == 1)
break; //已经平衡,直接跳出
if (pre_tmp->bf != 0) { //m_bf为+2/-2
if (pre_tmp->bf < 0) {
lable = -1; //lable表示父节点符号,下面会用来区分同号异号
it_tmp = pre_tmp->left;
}
else {
lable = 1;
it_tmp = pre_tmp->right;
}
if (it_tmp->bf == 0) { //pre_tmp的较高子树的头节点m_bf为0
if (lable == -1) {
RotateR(pre_tmp);
pre_tmp->bf = 1;
pre_tmp->right->bf = -1;
}
else {
RotateL(pre_tmp);
pre_tmp->bf = -1;
pre_tmp->left->bf = 1;
}
break; //直接跳出,并没有链接,需要下文写上链接
}
if (it_tmp->bf == lable) { //同号
lable == 1 ? RotateL(pre_tmp) : RotateLR(pre_tmp);
}
else { //异号
lable == -1 ? RotateLR(pre_tmp): RotateRL(pre_tmp);
}
//sign == -1 ? ppre_tmp->left_child = pre_tmp //不能写成这样,因为sign值可能为0,会直接进入后者
// : ppre_tmp->right_child = pre_tmp; //!!!!! sign maybe 0 ! not only1 or -1 !!! warning!
if (sign == -1)
ppre_tmp->left = pre_tmp;
else if (sign == 1) //else if正确方式
ppre_tmp->right = pre_tmp;
}
it_tmp = pre_tmp; //回溯
}
if (st.empty()) //栈为空,根节点
t = pre_tmp;
else { //这一段else参考书上没有,书是错的,如果不写此处,290break直接跳出while循环,会导致链接不上,数据丢失
ppre_tmp = st.top();
if (ppre_tmp->data > pre_tmp->data)
ppre_tmp->left = pre_tmp;
else
ppre_tmp->right = pre_tmp;
}
}
delete tmp;
tmp = nullptr;
return true;
}
源cpp代码
#include"AVL.h"
#include<iostream>
using namespace std;
int main()
{
AVLTree<int> tree;
cout << "建立AVL树,终止表示设为0" << endl;
tree.Refvalue = 0;
cout << "1:建树\n2:插入\n3:搜索\n4:删除\n5:输出\n6:清空\n7:退出\n";
bool over = false;
while (!over)
{
int c;
int num;
cin >> c;
switch (c)
{
case 1: {
cin >> tree;
cout << "建树完毕\n";
break;
}
case 2: {
cin >> num; tree.insert(tree.root, num); cout << "插入完毕\n"; break; }
case 3: {cin >> num; if (tree.search(num, tree.root) != NULL)cout << num << "已经被找到\n"; else cout << num << "不存在\n"; break; }
case 4: {cin >> num; tree.Remove(tree.root, num); }
case 5: {cout << tree; cout << " 凹凸打印AVL树\n"; tree.PrintBinTree(tree.root, 1); break; }
case 6: {
//tree.Delete(tree.root);
tree.root=NULL;
break; }
case 7:{ over = true; break; }
default:cout << "输入有误\n";
break;
}
}
char ch;
cin >> ch;
}
AVL树C++实现(插入,删除,查找,清空,遍历操作)的更多相关文章
- 二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)
二叉查找树(BST).平衡二叉树(AVL树)(只有插入说明) 二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点, ...
- jQuery---jq操作标签文本(html(),text()),jq操作文档标签(插入,删除,修改),克隆,,jq操作属性,jq操作class属性,jq操作表单value,jq操作css,jq操作盒子(重要),jq操作滚动条
jQuery---jq操作标签文本(html(),text()),jq操作文档标签(插入,删除,修改),克隆,,jq操作属性,jq操作class属性,jq操作表单value,jq操作css,jq操作盒 ...
- AVL树的插入删除查找算法实现和分析-1
至于什么是AVL树和AVL树的一些概念问题在这里就不多说了,下面是我写的代码,里面的注释非常详细地说明了实现的思想和方法. 因为在操作时真正需要的是子树高度的差,所以这里采用-1,0,1来表示左子树和 ...
- 二叉搜索树-php实现 插入删除查找等操作
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的 ...
- 链表的C++实现——创建-插入-删除-输出-清空
注:学习了数据结构与算法分析后,对链表进行了C++实现,参考博文:http://www.cnblogs.com/tao560532/articles/2199280.html 环境:VS2013 // ...
- [PHP] 数据结构-链表创建-插入-删除-查找的PHP实现
链表获取元素1.声明结点p指向链表第一个结点,j初始化1开始2.j<i,p指向下一结点,因为此时p是指向的p的next,因此不需要等于3.如果到末尾了,p还为null,就是没有查找到 插入元素1 ...
- Java对二叉搜索树进行插入、查找、遍历、最大值和最小值的操作
1.首先,须要一个节点对象的类.这些对象包括数据.数据代表存储的内容,并且还有指向节点的两个子节点的引用 class Node { public int iData; public double dD ...
- javascript集合的交,并,补,子集,长度,新增,删除,清空等操作
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat=&qu ...
- C++ map操作——插入、查找、遍历
c++ map 操作学习 #include <iostream> #include <map> #include <string> #include <vec ...
- 数据结构与算法(九):AVL树详细讲解
数据结构与算法(一):基础简介 数据结构与算法(二):基于数组的实现ArrayList源码彻底分析 数据结构与算法(三):基于链表的实现LinkedList源码彻底分析 数据结构与算法(四):基于哈希 ...
随机推荐
- Mybatis(三) 映射文件详解
前面说了全局配置文件中内容的详解,大家应该清楚了,现在来说说这映射文件,这章就对输入映射.输出映射.动态sql这几个知识点进行说明,其中高级映射(一对一,一对多,多对多映射)在下一章进行说明. 一.输 ...
- Electron初探
H5开发桌面应用? 没错,H5现在也可以开发跨平台的桌面应用了,这意味着我们可以用网页来设计和制作桌面应用. 基于Node.js的Electron框架就可以实现桌面应用,比较有名的Electron框架 ...
- 音视频编解码: YUV存储格式中的YUV420P,YUV420SP,NV12, NV21理解(转)
概述 之前介绍了YUV码流的采样格式,下面分析下YUV码流的存储格式,YUV码流的存储格式与采样格式息息相关.总的来讲,YUV存储格式主要分为两种: planar 平面格式 指先连续存储所有像素点的 ...
- Python时间模块
1 time 模块: 读取系统时钟当前时间: 在 time 模块中,time.time()和 time.sleep()函数是最有用的模块. 1.1 time.time() time.time()函数返 ...
- python – 基于pandas中的列中的值从DataFrame中选择行
如何从基于pandas中某些列的值的DataFrame中选择行?在SQL中我将使用: select * from table where colume_name = some_value. 我试图看看 ...
- C语言 · 猜算式 · 乘法竖式
题目:猜算式 你一定还记得小学学习过的乘法计算过程,比如: 273 x 15 ------ 1365 273 ------ 4095 请你观察如下的乘法算式 *** x *** ------- ...
- 使用.gitignore删除Github上的.idea文件
环境:windows + git bash. 一.问题来源 由于之前用Goland建立Golang工程时,生成了.idea文件,不小心上传至Github: 所以尝试用.gitignore进行忽略不上传 ...
- MyBatis 配置多数据源
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- TCP连接的状态与关闭方式,及其对Server与Client的影响
1. TCP连接的状态 首先介绍一下TCP连接建立与关闭过程中的状态.TCP连接过程是状态的转换,促使状态发生转换的因素包括用户调用.特定数据包以及超时等,具体状态如下所示: CLOSED:初始状态, ...
- iOS开发之--属性关键字以及set和get方法
一.属性分为三大类 1.读写性控制 a.readOnly只读,只会生成get方法,不会生成set方法 b.readWrite可读可写,会生成set方法,也会生成get方法(默认设置) 2.setter ...