`
hellhell
  • 浏览: 24081 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

Neil Brown精妙的avl实现

阅读更多
最近在复习数据结构,看到avl树,这样比较复杂的数据结构通常都很搞人。于是上网,找到一个实现看,写这代码的老外叫Neil Brown,一个大胡子,我们知道,在国外,开发人员的水平经常和胡子的多少成正比。还看到他在blog中说,这个代码打算用在linux kernel里,自然不会是普通代码了。
我拿下来,狠看一阵才看懂,不得不说,这个代码很多地方的实现是很精妙的,和教材上只是为了让你理解概念的代码,区别很大。
这个实现有一个特点,就是没有一般树实现中的left和right指针,作者用一个数组next[2]来表示,next[0]就是左子树,next[1]就是右子树,然后每个节点也不保存当前的层高,这个是教科书的做法,int longer:2,极其省俭的表示了是左子树长(值为0),还是右子树长(值为1),或者当前节点上这颗树是平衡的(值了-1)。

/*
 * Usage:  avl3  list of integers ...
 *
 * Each integer will be checked to see if it is currently in
 * the AVL tree.  If not, it will be inserted. If so, it will
 * be deleted.  The tree starts out empty and the final tree is
 * printed (on its side) in an ASCII-art style
 */

#include <stdlib.h>
typedef int value_t;

#define LEFT 0  // 表示左节点,或者左子树长
#define RIGHT 1 // 表示右节点,或者右子树长
#define NEITHER -1 // 以当前节点为根的子树是平衡的
typedef int direction; //重定向一个,表示操作是在左子树还是右子树上
// 树节点的定义
typedef struct node_s {
   value_t        value;
   struct node_s *next[2];
   int            longer:2;
} *node;
#define Balanced(n) (n->longer < 0) // 平衡时值了-1


这里开始精妙了,我们平时的tree查找是怎么写的,target小于当前节点就下到左子树
继续找,大于就下到右子树继续找,这里就用一个direction next_step = (target > tree->value)得到了接下来操作的方向。next[next_step]就是接下来要查找的节点

node avl_find(node tree, value_t target)
{
	while (tree && target != tree->value) {
		direction next_step = (target > tree->value);
		tree = tree->next[next_step];
	}
	return tree;
}


作者不大喜欢single rotation和double rotation这样的说法,他把旋转分为两种,一种是2旋,就是single rotation,这个时候用数组的优势就出来了,不用把相似的single rotation写两遍,传入的dir值不同就可以了。



static node avl_rotate_2(node *path_top, direction dir)
{
	node B, C, D, E;
	B = *path_top;
	D = B->next[dir];
	C = D->next[1-dir];
	E = D->next[dir];

	*path_top = D;
	D->next[1-dir] = B;
	B->next[dir] = C;
	B->longer = NEITHER;
	D->longer = NEITHER;
	return E;
}


这里是三旋转,也就是double rotation.

注意旋转后面third变量的作用,third表示新入节点在C还是在E上。如果在E上,独立的C的高度必小于A,因为在E上还没有插入新节点时,C只比A高1,而且C上有D,F垫底,所以独立的C的高度要减2,所以独立的C比独立的A高度小1。

static node avl_rotate_3(node *path_top, direction dir, direction third)
{
	node B, F, D, C, E;
	B = *path_top;
	F = B->next[dir];
	D = F->next[1-dir];
	/* note: C and E can be NULL */
	C = D->next[1-dir];
	E = D->next[dir];
	*path_top = D;
	D->next[1-dir] = B;
	D->next[dir] = F;
	B->next[dir] = C;
	F->next[1-dir] = E;
	D->longer = NEITHER;

	/* assume both trees are balanced */
	B->longer = F->longer = NEITHER;

	if (third == NEITHER)
		return NULL;
	else if (third == dir) {
		/* E holds the insertion so B is unbalanced */ 
		B->longer = 1-dir;
		return E;
	} else {
		/* C holds the insertion so F is unbalanced */
		F->longer = dir;
		return C;
	}
}


插入新节点后已经,树已经不平衡了,需要重新计算节点上的longer值。
/***************************************************
 * INSERTION                                       *
 ***************************************************/
static inline void avl_rebalance_path(node path, value_t target)
{
	/* Each node in path is currently balanced.
	 * Until we find target, mark each node as longer
	 * in the direction of target because we know we have
	 * inserted target there
	 */
	while (path && target != path->value) {
		direction next_step = (target > path->value);
		path->longer = next_step;
		path = path->next[next_step];
	}
}


这里是插入新节点后使树重新平衡的全过程,包括旋转节点和更新高度信息。path_top是更新的起点。如果path_top节点是平衡的,不需要旋转,更新高度信息即可。如果在较短和路径上插入,也不需要旋转,还是更新高度信息,path_top变成平衡的,然后对插入的那棵树更新高度信息。如果在较长的路径上插入,就需要旋转了,first和second表示两次的操作方向,如果相同,就是2旋转,否则是3旋转,这时要注意third变量,如果C和E都为空,那么third变量就是NEITHER,不需要特殊处理,否则,third就传入3旋转的函数,用于下面更新高度信息。

static inline void avl_rebalance_insert(node *path_top, value_t target)
{
	node path = *path_top;
	direction first, second, third;
	if (Balanced(path)) 
		;
	else if (path->longer != (first = (target > path->value))) {
		/* took the shorter path */
		path->longer = NEITHER;
		path = path->next[first];
	} else if (first == (second = (target > path->next[first]->value))) {
		/* just a two-point rotate */
		path = avl_rotate_2(path_top, first);
	} else {
		/* fine details of the 3 point rotate depend on the third step.
		 * However there may not be a third step, if the third point of the
		 * rotation is the newly inserted point.  In that case we record
		 * the third step as NEITHER
		 */
		path = path->next[first]->next[second];
		if (target == path->value) third = NEITHER;
		else third = (target > path->value);
		path = avl_rotate_3(path_top, first, third);
	}
	avl_rebalance_path(path, target);
}


avl_insert是插入接口,这里又有一个技巧,对于avl树,不用从根节点上开始更新高度信息。开始更新高度信息的起点,是path_top。如果查找的一路上下来,每个节点都是平衡的,那么只能从根上开始更新,否则,从第一个不平衡的节点开始更新。插入完成后,再调用avl_rebalance_insert来平衡树。

int avl_insert(node *treep, value_t target)
{
	/* insert the target into the tree, returning 1 on success or 0 if it
	 * already existed
	 */
	node tree = *treep;
	node *path_top = treep;
	while (tree && target != tree->value) {
		direction next_step = (target > tree->value);
		if (!Balanced(tree)) path_top = treep;
		treep = &tree->next[next_step];
		tree = *treep;
	}
	if (tree) return 0;
	tree = malloc(sizeof(*tree));
	tree->next[0] = tree->next[1] = NULL;
	tree->longer = NEITHER;
	tree->value = target;
	*treep = tree;
	avl_rebalance_insert(path_top, target);
	return 1;
}


明天再写删除的,太晚了。
  • 大小: 3.7 KB
  • 大小: 5.1 KB
分享到:
评论

相关推荐

    O'neil Advanced Engineering Mathmatics

    根据提供的文件信息,“O'neil Advanced Engineering Mathematics”是一本国际学生版的教材,由Peter V. O’Neil编写,他来自美国阿拉巴马大学伯明翰分校。本书旨在为工程学领域的学生提供数学基础教育,帮助他们...

    数据库课件 作者:patrick O'neil

    触发器则是在特定事件(如插入、更新或删除数据)发生时自动执行的代码段,用于实现业务规则或数据完整性。 "chapter 5 Programs to Access a Database.ppt"关注的是如何通过编程语言访问数据库,如使用Java的JDBC...

    O'Neil_Advanced_Engineering_Mathematics_7th_txtbk

    《O'Neil_Advanced_Engineering_Mathematics_7th_txtbk》这本书是工程数学领域的一本经典教材,由著名数学家O'Neil撰写,涵盖了高等工程数学的多个核心概念和技术。以下是从该书的部分内容中提取的关键知识点,旨在...

    Android Studio 3.0 Development - Neil Smyth

    《Android Studio 3.0 Development》是Neil Smyth撰写的一本专为Android开发者量身打造的指南,专注于介绍Android Studio 3.0的最新特性和开发技巧。这本书深入浅出地讲解了如何利用这个强大的集成开发环境(IDE)来...

    Advanced Engineering Mathematics 6ed O'Neil

    本书可能会介绍数值方法的基础原理、误差分析以及具体的算法实现。 5. **概率论与统计**:在不确定性建模和数据分析方面,概率论和统计学起着关键作用。本书可能会涉及随机变量、概率分布、参数估计等内容。 6. **...

    O'Neil Advanced Engineering Mathematics(6th) soultion

    Part 2/2 Chapter 14-27

    Artificial Intelligence Through Prolog - Neil C Rowe.pdf

    Artificial Intelligence Through Prolog - Neil C Rowe.pdf Artificial Intelligence Through Prolog - Neil C Rowe.pdf

    Guerrilla Capacity Planning Neil J. Gunther

    《Guerrilla Capacity Planning》是由Neil J. Gunther所著的一本关于容量规划的书籍。容量规划是IT行业中一个重要的领域,尤其对于高度可扩展的应用和服务的规划至关重要。本书为读者提供了一种战术方法,以应对在...

    Electronics: A System Approach_Neil Sorey

    此外,通过自顶向下的系统方法,本书不仅仅介绍了电子组件和电路的细节,更重要的是提供了一个框架,让读者可以理解这些组件和电路是如何结合在一起,共同实现一个电子系统功能的。 自顶向下的方法论强调了整体理解...

    Delphi Basics Neil Moffatt 2002-2013.chm

    Delphi Basics Neil Moffatt 2002-2013.chm

    neil:尼尔的通用工具

    尼尔:工作流程实用程序 尼尔·米切尔(Neil Mitchell)运行的一种执行常见动作的工具。 这些命令中的许多命令都增强了我的工作流程的特定方面。 欢迎其他人使用此工具,但不支持该工具。 使用neil --help查看可用的...

    Adv Engineering Math 7ed O'Neil

    《高级工程数学第七版》(Adv Engineering Math 7ed O'Neil)是一本全面涵盖工程数学领域的教科书,由著名数学家O'Neil撰写。本书深入探讨了多个数学概念和理论,尤其在微积分、线性代数、复变函数、数值分析以及...

    O'Neil Advanced Engineering Mathematics Solution 1/2

    O'Neil Instructor's Manual. Including solutions for all exercises. Chapter 1-13

    Electronics. A Systems Approach 4e by Neil Storey (prentice hall).pdf

    3. Neil Storey的系统方法:Neil Storey使用了一种备受尊敬的系统方法,先从整体概念开始讲解,以建立学生的信心和理解,然后深入到更详细的分析。这种方法有助于学生首先理解系统的设计目标,然后深入研究各个组件...

    Adv Engineering Math 7ed O'Neil Solution

    O'Neil。该手册涵盖了微分方程、拉普拉斯变换、级数解法、数值解近似方法以及向量与向量空间等内容。下面将对文件中提到的主要章节进行详细的分析与总结。 ### 第一章:一阶微分方程 #### 1.1 术语与可分离方程 -...

    Neil_Shephard_(auth

    这些书籍涵盖了统计学和应用概率论的广泛领域,由知名学者编辑和撰写,包括D.R. Cox、D.V. Hinkley等人。以下是对部分书籍及其相关知识点的详细阐述: 1. **Stochastic Population Models in Ecology and ...

Global site tag (gtag.js) - Google Analytics