《数据结构与算法分析》学习笔记(五)——二叉树
(一)查找二叉树ADT
1、二叉查找树ADT性质:
对于树中的每个节点X,它的左子树中所有关键字值都小于X的关键字值,而它的右子树值的关键字值都大于X的关键字值。
2、一些ADT的基本操作
结构定义
typedef int SearchTree_ElementType; struct SearchTreeNode; //这句话一定要加,要不下面这句不会成立。
typedef struct SearchTreeNode* SearchTree; struct SearchTreeNode
{
SearchTree_ElementType Element;
SearchTree Left;
SearchTree Right;
};
(1)建立空树:
一般来说空树有些人习惯用保留一个空的节点,但是按照标准定义的话,树理应为NULL,采用后者。(附简单代码,复习递归思路)
//清空树
SearchTree SearchTree_MakeEmpty(SearchTree Root)
{
if (Root != NULL)
{
SearchTree_MakeEmpty(Root->Left);
SearchTree_MakeEmpty(Root->Right);
delete Root;
}
return NULL;
}
(2)Find
使用简单递归思想写,代码简单,但是空间栈比较多,但是因为深度为logN,所以应该空间是可以接受的。
SearchTree_Position SearchTree_Find(SearchTree_ElementType X, SearchTree T)
{
if (T == NULL)
return NULL;
else if (X < T->Element)
SearchTree_Find(X, T->Left);
else if (X > T->Element)
SearchTree_Find(X, T->Right);
else
return T;
}
(3)FindMax和Min
//FindMax和FindMin
SearchTree_Position SearchTree_FindMax(SearchTree T)
{
if (T == NULL)
return NULL;
else if (T->Right == NULL)
return T;
else
SearchTree_FindMax(T->Right);
} SearchTree_Position SearchTree_FindMin(SearchTree T)
{
if (T == NULL)
return NULL;
else if (T->Left == NULL)
return T;
else
SearchTree_FindMax(T->Left);
}
(4)插入
如果有重复单元进行插入,那可以选择加入一个count,记录加入多少次,而不用重复进行操作
//插入
SearchTree_Position SearchTree_Insert(SearchTree T,SearchTree_ElementType X)
{
if (T == NULL)
{
T = new SearchTreeNode;
if (T == NULL)
{
cout << "Out of Space" << endl;
}
else
{
T->Element = X;
T->Left = T->Right = NULL;
}
}
else if (T->Element > X)
SearchTree_Insert(T->Right, X);
else if (T->Element < X)
SearchTree_Insert(T->Left, X); return T;
}
(5)删除
删除的情况有3种:
a、没有儿子的,直接删除
b、有一个儿子的,删除这个节点之后,把子节点接上去
d、有两个儿子的,可以用右子树的最小值或者左子树的最大值代替该节点的数据并递归删除那个节点。
如果删除的次数有限,那么可以使用一个tag进行记录即可,即可以使用策略称为惰性测量。
//删除
SearchTree_Position SearchTree_Delete(SearchTree T, SearchTree_ElementType X)
{
SearchTree_Position NodeTemp; if (T == NULL)
cout << "Not Found" << endl;
else if (T->Element > X)
SearchTree_Delete(T->Left, X);
else if (T->Element < X)
SearchTree_Delete(T->Right, X);
else
{
if (T->Left && T->Right) //两个儿子
{
NodeTemp = SearchTree_FindMin(T->Right);
T->Element = NodeTemp->Element;
SearchTree_Delete(T->Right, T->Element);
}
else
{
NodeTemp = T;
if (T->Left == NULL)
T = T->Right;
else if (T->Right == NULL)
T = T->Left; delete NodeTemp;
}
} return T;
}
(二)——AVL树
1、AVL树定义:
每个节点的左子树和右子树的高度最多差1的二叉查找树(空树的高度定义为 -1)
2、旋转:
如果删除使用惰性删除,那么可能破坏AVL树的基本操作就只能是——插入。所以采用一种操作称为旋转来对树进行简单修正。
(1)AVL不平衡情况分析
假设必须平衡的点成为Node_A,那么有四种情况,(a d镜像 b c镜像)
a、对Node_A的左儿子的左子树进行一次插入
b、对Node_A的左儿子的右子树进行一次插入
c、对Node_A的右儿子的左子树进行一次插入
d、对Node_A的右儿子的右子树进行一次插入
对于a,d使用单旋转进行修正,对于b,c使用双旋转进行修正。
(2)单旋转
A、图示效果
其中左图所示的是情况a,X和Z的高度差2,所以要进行修正,有图是通过单旋转进行的修正。
方法:就是将K1拉起来,K2成为K1的右子树,Y成为K2的左子树。
B、参考代码
/**********************声明,类型定义**********************/
struct Avl_Node;
typedef struct AvlNode* Position;
typedef struct AvlNode* AvlTree; typedef int Avl_ElementType; /***********************函数声明**********************/ #define Max(a,b) (a>b?a:b) //单旋转
Position Avl_SingleRotateWithLeft(Position K2);
Position Avl_SingleRotateWithRight(Position K1); /***********************AVL数据结构定义**********************/
struct AvlNode
{
Avl_ElementType Element;
AvlTree Left;
AvlTree Right;
int Height;
};
这个是ADT的数据格式声明
//单旋转
Position Avl_SingleRotateWithLeft(Position K2)
{
Position K1; K1 = K2->Left;
K2->Left = K1->Right;
K1->Right = K2; K2->Height = Max(Avl_Get_NodeHeight(K2->Left), Avl_Get_NodeHeight(K2->Right)) + 1; K1->Height = Max(Avl_Get_NodeHeight(K1->Left), K2->Height) + 1; return K1;
}
Position Avl_SingleRotateWithRight(Position K1)
{
Position K2; K2 = K1->Right;
K1->Right = K2->Left;
K2->Left = K1; K1->Height = Max(Avl_Get_NodeHeight(K1->Left), Avl_Get_NodeHeight(K1->Right))+1;
K2->Height = Max(Avl_Get_NodeHeight(K2->Right), K1->Height) + 1; return K2;
}
(3)双旋转
A、图示效果
其中左图就是情况b,BC和D的高度差2,所以要修正,但是通过单旋转并没有办法进行修正,所以使用双旋转进行修正
方法:将K3成为根节点,K1,K2成为K3的子节点,B成为K1的右子树,C成为K2的左子树。
B、参考代码、
//双旋转
Position Avl_DoubleRotateWithLeft(Position K3)
{
K3->Left = Avl_SingleRotateWithRight(K3->Left); return Avl_SingleRotateWithLeft(K3);
}
Position Avl_DoubleRotateWithRight(Position K1)
{
K1->Right = Avl_SingleRotateWithLeft(K1->Right); return Avl_SingleRotateWithRight(K1);
}
3、AVL树的一些基本操作函数
#include "stdafx.h"
#include "AVL.h"
#include <iostream> using namespace std; //空树
AvlTree Avl_MakeEmpty(AvlTree T)
{
if (T != NULL)
{
Avl_MakeEmpty(T->Left);
Avl_MakeEmpty(T->Right);
free(T); }
return NULL;
} //查找
AvlTree Avl_Find(Avl_ElementType X, AvlTree T)
{
if (T == NULL)
{
return NULL;
cout << "Not Found!" << endl;
} if (X < T->Element)
{
return Avl_Find(X, T->Left);
}
else if (X > T->Element)
{
return Avl_Find(X, T->Right);
}
else
{
return T;
} } //查找极值
AvlTree Avl_FindMin(AvlTree T)
{
if (T == NULL)
{
return NULL;
}
else if (T->Right == NULL)
{
return T;
}
else
{
Avl_FindMin(T->Right);
}
} AvlTree Avl_FindMax(AvlTree T)
{
if (T == NULL)
{
return NULL;
}
else if (T->Left == NULL)
{
return T;
}
else
{
Avl_FindMin(T->Left);
}
} //获取树的高度,计算使用递归的方法
int Avl_Get_NodeHeight(Position P)
{
if (P == NULL)
return -1;
else
return P->Height;
} //单旋转
Position Avl_SingleRotateWithLeft(Position K2)
{
Position K1; K1 = K2->Left;
K2->Left = K1->Right;
K1->Right = K2; K2->Height = Max(Avl_Get_NodeHeight(K2->Left), Avl_Get_NodeHeight(K2->Right)) + 1; K1->Height = Max(Avl_Get_NodeHeight(K1->Left), K2->Height) + 1; return K1;
}
Position Avl_SingleRotateWithRight(Position K1)
{
Position K2; K2 = K1->Right;
K1->Right = K2->Left;
K2->Left = K1; K1->Height = Max(Avl_Get_NodeHeight(K1->Left), Avl_Get_NodeHeight(K1->Right))+1;
K2->Height = Max(Avl_Get_NodeHeight(K2->Right), K1->Height) + 1; return K2;
} //双旋转
Position Avl_DoubleRotateWithLeft(Position K3)
{
K3->Left = Avl_SingleRotateWithRight(K3->Left); return Avl_SingleRotateWithLeft(K3);
}
Position Avl_DoubleRotateWithRight(Position K1)
{
K1->Right = Avl_SingleRotateWithLeft(K1->Right); return Avl_SingleRotateWithRight(K1);
} //插入
AvlTree Avl_Insert(Avl_ElementType X, AvlTree T)
{ //空树
if (T == NULL)
{
T = new struct AvlNode;
if (T == NULL)
{
cout << "out of Space" << endl;
}
else
{
T->Element = X;
T->Height = 0;
T->Left = T->Right = NULL;
}
}
//在左子树
else if (X < T->Element)
{
Avl_Insert(X, T->Left);
if (Avl_Get_NodeHeight(T->Left) - Avl_Get_NodeHeight(T->Right) == 2)
{
if (X < T->Left->Element) //a情况
T = Avl_SingleRotateWithLeft(T);
else //b情况
T = Avl_DoubleRotateWithLeft(T);
}
}
else if (X>T->Element)
{
Avl_Insert(X, T->Right);
if (Avl_Get_NodeHeight(T->Right) - Avl_Get_NodeHeight(T->Left) == 2)
{
if (X < T->Right->Element) //c情况
T = Avl_DoubleRotateWithRight(T);
else //d情况
T = Avl_SingleRotateWithRight(T);
}
} T->Height = Max(Avl_Get_NodeHeight(T->Left), Avl_Get_NodeHeight(T->Right)) + 1;
return T;
}
(三)——树的遍历
1、三种遍历:先序、中序、后序
void PrintTree(SearchTree T)
{
if( T!= NULL)
{
PrintTree(T->Left);
PrintElement(T->Elemment);
PrintTree(T->Right);
}
}
《数据结构与算法分析》学习笔记(五)——二叉树的更多相关文章
- (转)Qt Model/View 学习笔记 (五)——View 类
Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...
- <数据结构与算法分析>读书笔记--利用Java5泛型实现泛型构件
一.简单的泛型类和接口 当指定一个泛型类时,类的声明则包括一个或多个类型参数,这些参数被放入在类名后面的一对尖括号内. 示例一: package cn.generic.example; public ...
- C#可扩展编程之MEF学习笔记(五):MEF高级进阶
好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...
- java之jvm学习笔记五(实践写自己的类装载器)
java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(五) indigo computer vision
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- Typescript 学习笔记五:类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- <数据结构与算法分析>读书笔记--最大子序列和问题的求解
现在我们将要叙述四个算法来求解早先提出的最大子序列和问题. 第一个算法,它只是穷举式地尝试所有的可能.for循环中的循环变量反映了Java中数组从0开始而不是从1开始这样一个事实.还有,本算法并不计算 ...
- <数据结构与算法分析>读书笔记--运行时间计算
有几种方法估计一个程序的运行时间.前面的表是凭经验得到的(可以参考:<数据结构与算法分析>读书笔记--要分析的问题) 如果认为两个程序花费大致相同的时间,要确定哪个程序更快的最好方法很可能 ...
- ES6学习笔记<五> Module的操作——import、export、as
import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...
- <数据结构与算法分析>读书笔记--函数对象
关于函数对象,百度百科对它是这样定义的: 重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象.又称仿函数. 听起来确实很难懂,通过搜索我找到一篇 ...
随机推荐
- CentOS 7部署flume
CentOS 7部署flume 准备工作: 安装java并设置java环境变量,在`/etc/profile`中加入 export JAVA_HOME=/usr/java/jdk1.8.0_65 ex ...
- php在没用xdebug等调试工具的情况下如何让调试内容优雅地展现出来?--php数组格式化
不知道各位猿猿们有没有碰到过类似的情况.装的PHP环境没有xdebug,而又经常用到数组.调试的时候也需要经常查看数组的结构和字段内容,用var_dump打印出来的数组内容总是杂乱无章.实在无法忍受, ...
- Unity3d《Shader篇》地球旋转上空悬浮云层
记得把纹理设置成Repeat Shader "Custom/Earth" { Properties { _MainTex ("Base (RGB)", 2D) ...
- Oracle备份之RMAN
1.备份:物理备份时文件层次的备份,逻辑备份时数据层次的备份,物理备份为主,逻辑备份作为补充.物理备份分为用户管理备份和RMAN备份,前者使用SQL命令和OS的cp命令进行文件备份,后者使用RMAN工 ...
- codeforces 518B. Tanya and Postcard 解题报告
题目链接:http://codeforces.com/problemset/problem/518/B 题目意思:给出字符串 s 和 t,如果 t 中有跟 s 完全相同的字母,数量等于或者多过 s,就 ...
- HTTP协议与HTML表单(再谈GET与POST的区别)
HTTP的GET/POST方式有何区别?这是一个老生常谈的问题,但老生常谈的问题往往有一些让人误解的结论.本文将带您浅尝HTTP协议,在了 解HTTP协议的同时将会展示许多被人们忽视的内容.在掌握了H ...
- rsync实现同步
一.备份客户端: 1.创建/etc/rsyncd.secrets 权限配置600 (写服务器端的账户密码) 2.客户端配置文件: port=873log file=/var/log/rsync.log ...
- Crystal Report 遇到需要登录的问题
解决方式: The advices for crystal report database connection settings: 1, Using ApplyLogOnInfo method in ...
- 3dmax导出3ds具有过多要导出的面超过64k解决方法
参考:http://blog.sina.com.cn/s/blog_7a71dd090100w3r0.html 修改器->网格编辑->ProOptimizer 选中对象, 原始模型 顶点数 ...
- Mysql事务隔离级别
在说Isolation之前,需要谈谈关系型数据库的ACID特性. A(atomicity,原子性),指一个事务要么完全完成,要么全部回滚到起始状态,不存在中间状态. C(Consistency,一致性 ...