《数据结构与算法分析》学习笔记(五)——二叉树
(一)查找二叉树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),即它们是行为类似函数的对象.又称仿函数. 听起来确实很难懂,通过搜索我找到一篇 ...
随机推荐
- Window 下 Qt5 使用QMediaplayer 进行视频播放 流播放问题
int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget *widget = new QWidget; widget ...
- iOS GCD 必读推荐,有关于单例使用问题
链接如下:http://www.cocoachina.com/swift/20150129/11057.html 以前只注意使用dispatch_once达到创建单例对象时的线程安全,读了下边这篇文章 ...
- 数据结构顺序表删除所有特定元素x
顺序表类定义: template<class T> class SeqList : { public: SeqList(int mSize); ~SeqList() { delete[] ...
- Android 6.0的运行时权限
原文 http://droidyue.com/blog/2016/01/17/understanding-marshmallow-runtime-permission/ 主题 安卓开发 Andr ...
- yii压缩
application\components\controller.php protected function afterRender($view, &$output) { if(Yii:: ...
- 阿里云ecs Linux平台安装mongodb数据库
MongoDB提供了linux平台上32位和64位的安装包,你可以在官网下载安装包. 下载地址:http://www.mongodb.org/downloads 下载完安装包,并解压 tgz(以下演示 ...
- Git 、 Cocoapods常用命令
Git常用命令 1.添加文件 git add xxx 2.提交更新到本地 git commit -m 'local-repo' 3.提交更新 git push master ...
- [Android]在代码混淆中关闭 Log
-assumenosideeffects class android.util.Log{ public static *** d(...); public static *** e(...); }
- Jmeter 中使用非GUI启动进行压力测试
使用非 GUI 模式,即命令行模式运行 JMeter 测试脚本能够大大缩减所需要的系统资源.使用命令jmeter -n -t <testplan filename> -l <list ...
- Linux系统入门学习:在curl中设置自定义的HTTP头
http://www.linuxidc.com/Linux/2015-02/114220.htm