《数据结构与算法分析》学习笔记(五)——二叉树
(一)查找二叉树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),即它们是行为类似函数的对象.又称仿函数. 听起来确实很难懂,通过搜索我找到一篇 ...
随机推荐
- Appium+Robotframework实现Android应用的自动化测试-4:AppiumLibrary介绍和安装
Appium是个好东东,Android,iOS都支持,并且居然RobotFramework也支持Appium了,这就是本文要介绍的AppiumLibrary. 通过前面的文章大家知道可以使用多种语言来 ...
- fork详解
[本文链接] http://www.cnblogs.com/hellogiser/p/fork.html [代码] 下面的代码输出多少个-? C++ Code 123456789101112131 ...
- Codeforces 55D
基本的数位DP,注意记录那些状态可以用最小的空间判断出整除性. #include <cstdio> #include <cstring> using namespace std ...
- linux日常小坑
一.权限 1.文件权限 改动文件权限和所有权的命令有如下两个: chmod -更改权限 chown -更改所有权 不过,只有用户是当前所有者或者根用户,才能实际更改文件的权限或所有权,这一点大家要注意 ...
- MySQL排序原理与MySQL5.6案例分析【转】
本文来自:http://www.cnblogs.com/cchust/p/5304594.html,其中对于自己觉得是重点的加了标记,方便自己查阅.更多详细的说明可以看沃趣科技的文章说明. 前言 ...
- ffmpeg-20160520-git-bin
ESC 退出 0 进度条开关 1 屏幕原始大小 2 屏幕1/2大小 3 屏幕1/3大小 4 屏幕1/4大小 S 下一帧 [ -2秒 ] +2秒 ; -1秒 ' +1秒 下一个帧 -> -5秒 f ...
- GNOME启动时激活NumLock(小键盘数字锁定)
首先下载numlockx官方源提供的安装包,解压后进入目录运行终端,切换到root账户执行以下命令: python ./setup.py 然后依次点击GNOME菜单项上的“系统->首选项-> ...
- [转]Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能
版权声明:本文出自郭霖的博客,转载必须注明出处. 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最近项目中需要用到L ...
- 最牛逼android上的图表库MpChart(三) 条形图
最牛逼android上的图表库MpChart三 条形图 BarChart条形图介绍 BarChart条形图实例 BarChart效果 最牛逼android上的图表库MpChart(三) 条形图 最近工 ...
- 【leetcode】Binary Tree Preorder Traversal (middle)★
Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...