一些特殊的二叉树

0. 几种常见数据结构各种操作的时间复杂度:

在这里插入图片描述


1. 二叉查找树:

“二叉查找树” 又名 “二叉搜索树”(BST=Binary Search Tree)。
二叉查找树的结点按照“左根右”有序排列,因此中序访问是元素有序的。

二叉树的遍历方式:

(1)前序遍历 —> 根 左 右;
(2)中序遍历 —> 左 根 右;
(3)后序遍历 —> 左 右 根;
(4)层序遍历 —> 从根结点从上向下逐层遍历,在同一层按照从左到右顺序逐个节点访问

Tips:
所谓的“前、中、后”序,指的是二叉树的 根结点 在遍历过程中所处的位置。

————————————————————

2. 平衡树:

平衡树指的是任意结点的子树的高度差都小于等于1。
常见的平衡树有:AVL树(二叉平衡搜索树),B树(多路平衡搜索树)

————————————————————

3. AVL树:( 平衡二叉搜索树)

AVL这个简称并不是平衡二叉搜索树的英文缩写,而是得名与它的发明者 G. M. Adelson-Velsky 和 E. M. Landis。

AVL既是平衡树,也是搜索树。

————————————————————

4. B树:(多叉平衡搜索树)

B树(Balance-Tree)既是平衡树,也是搜索树。

“B-树”就是“B树”,中间的横线并不是减号。

MySQL数据库中的“索引”是基于 Hash表 或者 B+树 数据结构的,这是因为树的查找效率高,而且可以保持有序(B树的访问、查找、插入、删除操作的时间复杂度都是O(logN))。

MySQL等数据库的索引操作使用B+树而没有使用二叉查找树的原因不是因为查找速度,其实从算法逻辑上讲二叉查找树的速度和比较次数都是最小的,但是不得不考虑一个现实问题:磁盘IO。

利用索引查询的时候,不可能吧整个索引全部都加载到内存中,只能逐一加载每一个磁盘页(对应树的结点)。

什么是磁盘页:

一句话概括:操作系统访问内存的最小单位。

操作系统经常与 内存、硬盘 两种存储设备进行通信。
与内存操作,是虚拟一个 页 的概念作为最小单位;
与磁盘操作,虚拟一个 块 的概念作为最小单位。(多个扇区合并为一个块)


5. 红黑树:

(1)红黑树简介:

红黑树(Red Black Tree)是一种自平衡二叉树。

红黑树是一种特化的AVL树,所以它的基本形式包括:
① 二叉树
② 查找树(搜索树):按照 左根右 有序排列
③ 平衡树

红黑树在进行插入和删除操作时通过特定操作来保持二叉查找树的平衡(自平衡),从而获得较高的查找性能。

它虽然是复杂的,但它在最坏情况下的运行时间也是非常良好的,并且在实践中是高效的:它可以在 O(logn) 时间内 查找、插入 和 删除 (n为红黑树中元素的数目)。

(2)红黑树的特征:

红黑树是每个结点都带有 颜色属性的二叉查找树,结点颜色为 红色 或 黑色。
在二叉查找树强制的一般要求以外,对于任何有效的红黑树增加了如下的额外要求:

① 每个结点是 红色 或 黑色;
② 根结点是 黑色;
③ 所有叶子结点都是 黑色(叶子是NIL结点);
④ 如果有一个结点是红色,则它的两个子结点都是黑色;
(所以红色的结点不能相邻;黑色的结点可以相邻)
⑤ 对每个结点,从该结点到其子孙结点(即叶子结点)的所有途径上 包含相同数目的黑结点。
(所以红黑树平衡的是 黑色结点 的高度,红色结点主要是用来做区分的。)
(3)红黑树的应用场景:

① 利用红黑树 顺序 的功能:
例如内核中的进程调度器
(每个进程或线程有一个 运行体R,其中包含:
运行状态 新建、就绪、挂起、退出;运行体回调函数;回调参数;

调度器S:当前运行体、栈指针、栈大小;执行集合(就绪、延时、睡眠、等待。))

② 利用红黑树 快速查找 的功能:key-value

(4)红黑树的实现:

AVL树是一种 强平衡树,任何结点的左右子树的高度差不超过1; 红黑树不是强平衡树。

红黑树的旋转 示意图:

在这里插入图片描述

二叉查找树的实现:
//二叉查找树的实现:(只保证 有序,不保证 平衡)

typedef int VAL_TYPE;	

#if 0
struct bstree_node {
	VAL_TYPE 	data;			//在封装一个组件时,不要直接写int、char这些数据类型,而是在前面typedef定义一下,这样做是为了后面修改类型时更改方便
	struct bstree_node *left;
	struct bstree_node *right;
};

struct bstree {
	struct bstree_node *root;
};

# else
//业务与数据结构 分离:

//BSTREE_ENTRY 这个结构仅仅定义二叉树结构,其中不包括任何业务数据,只有基本的树的结构实现(左右结点)
#define BSTREE_ENTRY(name, type) 	\
	struct name {					\
		struct type *left;			\
		struct type *right;			\
	}

//业务中需要用到的树:在BSTREE_ENTRY 的基础上加上实际业务数据成员:data
struct bstree_node {
	VAL_TYPE 		data;
	BSTREE_ENTRY(, bstree_node) bst;
};

struct bstree {
	struct bstree_node *root;
};

/*
替换过程:
“树就是树,结点结构体中不能带有任何关于业务的成员”
“业务结点中包含树结点,以便串联起来组成一个树”

struct bstree_node {
	struct {
		struct bstree_node *left;
		struct bstree_node *right;
	};

	VAL_TYPE data;
};

bstree_node 是一个与实际业务相关的结点:
struct bstree_node {
	struct 纯结点;	
	业务数据;	
};

//纯树结点应该是一个泛型,结点类型是未知的,
//在C语言中,《如果要实现一个泛型结构体》,可以使用 #define:

#define BSTREE_ENTRY(type) 
	struct {
		struct type *left;
		struct type *right;
	}

BSTREE_ENTRY(bstree_node) node;
==> 等价于:
struct {
	struct bstree_node *left;
	struct bstree_node *right;
} node;


//如果在C++中,可以使用template的方式实现泛型结构体:
//1. 模板函数:
template<typename T> void func(T a) {
	cout << a << endl;
}
//2. 模板类:
template<typename T> class A {
public:
	void print() {
		cout << a << endl;
	}
private:
	T a;
};

A<int> a;
a.print();


template <typename T> struct BSTREE_ENTRY {
	struct T *left;
	struct T *right;
};
*/

/*
C语言中的#define:

define,宏定义,C语言中预处理命令一种,分为 无参宏定义 和 带参宏定义。

无参宏定义的一般形式为:
#define 宏名 字符串

带参宏定义的一般形式为:
#define 宏名(参数表) 字符串

在宏定义中的参数称为 形式参数,在宏调用中的参数为 实际参数。
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去替换形参。

*/

# endif

//inner:
struct bstree_node *bstree_create_node(VAL_TYPE data) {
	struct bstree_node *node = (struct bstree_node*)malloc(sizeof(struct bstree_node));
	if(node == NULL) {
		printf("malloc fail\n");
		return NULL:
	}

	node->data = data;
	node->bst.left = NULL;
	node->bst.right = NULL;

	return node;
}

//insert:
//注意:查找树的插入一定会插入到叶子结点上,所以不存在改变树结构的情况
int bstree_insert(bstree *T, VAL_TYPE data) {	//(要插入到哪个树,插入的值)
	if(T == null) return -1;
	if(T->root == NULL) {
		T->root = bstree_create_node(data);
		if(T->root == NULL) {
			return -1;
		}
		else {
			return 0;
		}
	}

	struct bstree_node *node = T->root;

	while(node != NULL) {	//查找应该插入的位置
		struct bstree_node *tmp = node;

		if(data < node->data) {
			node = node->bst.left;
		}
		else if(data > node->data) {
			node = node->bst.right;
		}
		else {
			printf("exit!\n");	//这棵树不支持存储两个相等的结点
			return -2;
		}
	}

	if(data < tmp->data) {	//找到位置后malloc并插入
		tmp->bst.left = bstree_create_node(data);
	}
	else {
		tmp->bst.right = bstree_create_node(data);
	}

	return 0;
}
/*
			7
		3		11
	1	  5   8      14
*/


//search:
struct bstree_node *bstree_search(struct bstree *T, VAL_TYPE data) {
	if(T == NULL) return NULL;

	struct bstree_node *node = T->root;

	while(node != NULL) {	
		if(data < node->data) {
			node = node->bst.left;
		}
		else if(data > node->data) {
			node = node->bst.right;
		}
		else {
			return node;
		}
	}

	return NULL;
}

//traversal: 
//递归式写法:(递推式需要借助栈)
//中序遍历:
int bstree_traversal_node(struct bstree_node *node) {
	if(node == NULL) return 0;

	bstree_traversal_node(node->bst.left);
	printf("%4d", node->data);
	bstree_traversal_node(node->bst.right);
}

int bstree_traversal(struct bstree *T)
{
	if(T == NULL || T->root == NULL) return -1;
	bstree_traversal_node(T->root);	
};



//delete:


//update:


int main() {

	return 0;
}

红黑树的实现:

//红黑树的实现:


typedef int KEY_VALUE;

typedef struct rbtree_node {
	unsigned char color;
	struct rbtree_node *left;
	struct rbtree_node *right;
	struct rbtree_node *parent;	//红黑树在二叉树的基础上加了一个父结点指针

	KEY_VALUE key;
	void  *value; 
} rbtree_node;

typedef struct rbtree {
	rbtree_node	*root;
	rbtree_node *nil;	//叶子结点是用来做判断用的
} rbtree;


//树的旋转:需要更改三个结点,加上parent方向,一共 6根指针:
//旋转是红黑树的基础,旋转主要是为了达到 黑高 的平衡。

void rbtree_left_rotate(rbtree *T, rbtree_node *x) {	//以x为轴心进行左旋
	rbtree_node *y = x->right;	//找到y结点,即x的右节点

	x->right = y->left;				// 1

	if(y->left != T->nil) {			// 2
		y->left->parent = x;
	}

	y->parent = x->parent;			// 3

	if(x->parent == T->nil) {		// 4
		T->root = y;		//当x是根结点,此时旋转后设置y为根结点
	}
	else if(x == x->parent->left) {
		x->parent->left = y; //x是它的父结点的左节点
	}
	else {
		x->parent->right = y; //x是它的父结点的右节点
	}

	y->left = x;					// 5
	x->parent = y;					// 6
}

//右旋:(在左旋的基础上做如下修改后,即变为右旋)
// x --> y
// y --> x
// left --> right
// right --> left
void rbtree_right_rotate(rbtree *T, rbtree_node *y) {

}


//insert:
//红黑树也是一个二叉查找树,它的插入与二叉查找树的插入相同:也是要插入到最下层(由于存在nil结点,所以不能称为叶子结点)

/*
插入一个新节点后的调整:
	插入一个结点时,有两种可能:
	(1)当所插结点的父结点是一个黑结点,此时使新插入结点的颜色属性为红,则黑高不受影响,不必旋转调整;
	(2)当所插结点的父结点是一个红结点,按照性质4,不能连续两个红红,所以新插结点必须为黑,此时黑高改变,需要进行旋转调整。

当遇到(2)的情况时,调整方式分 三种情况:
	首先,当红红时,插入者为红,其父结点为红,则其祖父结点一定为黑,
	(1)当其叔父结点为 红 时:  此时不需要旋转,只需要去改变就可以了:祖父由黑改红,父与叔父由红改黑(通过变色保持黑高相同)
	(2)当其叔父结点为 黑 时:
	(3)

*/

void rbtree_insert(rbtree *T, rbtree_node *z) {

}





//如何证明红黑树的正确性:归纳法。







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值