非线性结构
- 没有对应关系的 集合结构
- 一对多的 树结构
- 多对多的 图结构或网结构
一、树的概念
树形结构是一对多的非线性结构,非常类似于自然界中的树,数据元素之间既有分支关系,又有层次关系。
如下:
树具有下面两个特点:
- 树的根结点没有前驱结点,除根结点之外的所有结点有且只有一个前驱结点。
- 树中的所有结点都可以有零个或多个后继结点。
1.1 相关术语
1.1.1 树的结点
-
结点 (Node):表示树中的数据元素,由数据项和数据元素之间的关系组成。图上共14个结点。
-
根结点 (Root Node):每一个非空树都有且只有一个被称为根的结点。结点 A 就是整棵树的根结点。
-
父结点 (Parent Node):也叫双亲结点,结点的上层结点叫该结点的双亲。A 是 B、C、D 结点的父结点
-
兄弟结点(Brother Node):同一双亲的孩子。 B、C、D 互为兄弟结点。
-
堂兄弟结点 (Sibling Node):同一层的双亲不同的结点。L、M、N 互为堂兄弟结点。
-
孩子结点 (Child Node):结点的下层结点。 结点 B、C、D 是结点 A 的孩子结点。
-
叶子结点 (Leaf Node):也叫终端结点,如果结点没有任何子结点,那么此结点称为叶子结点(叶结点)。结点 K、L、F、M、N、I、J 都是这棵树的叶子结点。
-
分支结点(Branch Node):度不为 0 的结点,也叫非终端结点或内部结点。结点 A、B、C、D 、E、G、H是分支结点。
-
祖先结点 (Ancestor Node):从根到该结点所经分支上的所有结点。在图 5.1 中,结点 K的祖先结点是 A 、B、E。
-
子孙结点 (Descendant):以某结点为根的子树中的任一结点。除A 之外的所有结点都是 A 的子孙结点。
1.1.2 子树和空树
-
空树 (Empty tree):如果集合本身为空,那么构成的树就被称为空树。空树中没有结点。
-
子树 (Subtree):整棵树的根结点为结点 A,而如果单看结点 B、E、F、K、L 组成的部分来说,也是棵树,而且节点 B 为这棵树的根结点。所以称 B、E、F、K、L 这几个结点组成的树为整棵树的子树;结点 E、K、L 构成的也是一棵子树,根结点为 E。
注意:单个结点也是一棵树,只不过根结点就是它本身。图 1(A)中,结点 K、L、F 等都是树,且都是整棵树的子树。
1.1.3 结点的度和层次
-
结点的层次(Level of Node):从根结点到树中某结点所经路径上的分支数称为该结点的层次。根结点的层次规定为 1,其余结点的层次等于其双亲结点的层次加 1。
-
树的度(Degree of Tree):树中各结点度的最大值。上图树的度为4
1.1.4 有序树和无序树
-
无序树(Unordered Tree):树中任意一个结点的各孩子结点之间的次序构成无关紧要的树。通常树指无序树。
-
有序树(Ordered Tree):树中任意一个结点的各孩子结点有严格排列次序的树。二叉树是有序树,因为二叉树中每个孩子结点都确切定义为是该结点的左孩子结点还是右孩子结点。
1.1.5 森林
- 森林(Forest):m(m≥0)棵树的集合。自然界中的树和森林的概念差别很大,但在数据结构中树和森林的概念差别很小。从定义可知,一棵树有根结点和m 个子树构成,若把树的根结点删除,则树变成了包含 m 棵树的森林。当然,根据定义,一棵树也可以称为森林。
二、二叉树
2.1 二叉树概念
二叉树(Binary Tree)必要条件:
- 本身是有序树;
- 树中包含的各个节点的度不能超过 2,即只能是 0、1 或者 2;
2.2 二叉树性质
- 性质 1 一棵非空二叉树的第i层上最多有 2 i − 1 2^{i - 1} 2i−1 个结点(i≥1)。
- 性质 2 若规定空树的深度为 k,则深度为k的二叉树最多有 2 k 2^{k} 2k - 1 个结点(k≥0)。
- 性质 3 二叉树中,终端结点数(叶子结点数)为 n 0 n_{0} n0,度为 2 的结点数为 n 2 n_{2} n2,则 n 0 n_{0} n0 = n 2 n_{2} n2 + 1。
性质 3 的计算方法为:对于一个二叉树来说,除了度为 0 的叶子结点和度为 2 的结点,剩下的就是度为 1 的结点(设为 n 1 n_{1} n1),那么总结点 n = n 0 n_{0} n0 + n 1 n_{1} n1 + n 2 n_{2} n2。 同时,对于每一个结点来说都是由其父结点分支表示的,假设树中分枝数为 B,那么总结点数 n = B + 1。而分枝数是可以通过 n 1 n_{1} n1 和 n 2 n_{2} n2 表示的,即 B = n 1 n_{1} n1 + 2 * n 2 n_{2} n2。所以,n 用另外一种方式表示为 n = n 1 n_{1} n1 + 2 * n 2 n_{2} n2 + 1。 两种方式得到的 n 值组成一个方程组,就可以得出 n 0 n_{0} n0 = n 2 n_{2} n2 + 1。
2.2.1 满二叉树
如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。
满二叉树除了满足普通二叉树的性质,还具有以下性质:
- 满二叉树中第 i 层的节点数为 2 n − 1 2^{n - 1} 2n−1 个。
- 深度为 k 的满二叉树必有 2 k 2^{k } 2k - 1 个节点 ,叶子数为 2 k − 1 2^{k - 1} 2k−1。
- 满二叉树中不存在度为 1 的节点,每一个分支点中都两棵深度相同的子树,且叶子节点都在最底层。
- 具有 n 个节点的满二叉树的深度为 l o g 2 ( n + 1 ) log_{2}(n + 1) log2(n+1)。
2.2.2 完全二叉树
如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。
完全二叉树除了普通二叉树的性质,还具有以下性质:
- 具有 n 个结点的完全二叉树的深度k为 l o g 2 n log_{2}n log2n + 1 ( l o g 2 n log_{2}n log2n 表示取小于 l o g 2 n log_{2}n log2n 的最大整数)
- 对于具有 n 个结点的完全二叉树,如果按照从上到下和从左到右的顺序对所有结点从 1 开始编号,则对于序号为 i 的结点,有:
(1)如果 i > 1,则序号为 i 的结点的双亲结点的序号为 i / 2 (“/”表示整除);如果 i = 1,则该结点是根结点,无双亲结点。
(2)如果 2 * i ≤ n,则该结点的左孩子结点的序号为 2 * i;若 2 * i > n,则该结点无左孩子。
(3)如果 2 * i + 1 ≤ n,则该结点的右孩子结点的序号为 2 * i + 1;若 2 * i + 1 > n,则该结点无右孩子。
2.2.3 完满二叉树
除了叶子结点之外的每一个结点都有两个孩子结点。
2.3 无序二叉树链式实现
- 二叉树有两种存储方式,数组和链式存储,对于数组来说,我们利用二叉树的性质然后利用下标可以方便的找到一个节点的子节点和父节点。
- 顺序存储天生适配于完全二叉树,对于非完全二叉树并不合适,主要体现在空间上的浪费,所以我们需要用到另一种存储方式——链式存储。
这里讨论链式存储的递归实现方式
使用 # 号法实现二叉树
#在这里是表示叶子节点的左或右节点为空。
2.3.1 定义数据类型
typedef struct BitNode
{
struct BitNode *LeftChild;
char data;
struct BitNode *RightChild;
}BitNode, *BiTree;
//BiTree 相当于 struct BitNode * BiTree;
2.3.2 递归建立二叉树
先序方式,输入 ABDG##H###CE#I##F##
BiTree PrecreatTree (void) //创建二叉树
{
BiTree Tree = NULL;
char ch;
scanf("%c", &ch);
if (ch == '#') {
Tree = NULL;//说明该元素无效
return Tree;
} else {
Tree = (BitNode*)malloc(sizeof(BitNode));
if (Tree == NULL)//递归结束标识
return NULL;
Tree->data = ch;
Tree->LeftChild = NULL;//先置空
Tree->LeftChild = PrecreatTree ();
Tree->RightChild = NULL;
Tree->RightChild = PrecreatTree ();
return Tree;
}
}
2.3.3 遍历
2.3.3.1 深度优先遍历
先(前)序遍历
先访问树的根节点然后是左子树,最后是右子树
A B D G H C E I F
void PreOrderTraverse(BiTree T)//二叉树的先序遍历
{
if(T==NULL)
return ;
printf("%c ",T->data);
PreOrderTraverse(T->LeftChild);
PreOrderTraverse(T->RightChild);
}
中序遍历
先访问左子树,再访问该节点,最后是右子树 G D H B A E I C F
void InOrderTraverse(BiTree T)//二叉树的中序遍历
{
if(T==NULL)
return ;
InOrderTraverse(T->LeftChild);
printf("%c ",T->data);
InOrderTraverse(T->RightChild);
}
后序遍历
从根节点出发,依次遍历各节点的左右子树,直到当前节点左右子树遍历完成后,才访问该节点元素。
G H D B I E F C A
void PostOrderTraverse(BiTree T)//后序遍历
{
if(T==NULL)
return;
PostOrderTraverse(T->LeftChild);
PostOrderTraverse(T->RightChild);
printf("%c ",T->data);
}
2.3.3.2 广度优先遍历
层序遍历
typedef char datatype;
typedef struct queue
{
struct queue *next; // 指针域 队头
datatype data; //数据
}queue_l;
int front=0,rear=0;
//
// 层序遍历
void EnQueue(BiTree *a,BiTree node) //入队函数
{
a[rear++]=node;
}
BitNode *DeQueue(BitNode **a) //出队函数
{
return a[front++];
}
void displayNode(BiTree node) //输出函数
{
printf("%c ",node->data);
}
void LevelOdTraversal (BiTree T) //层序遍历
{
BiTree a[20];
BiTree p;
EnQueue(a, T);
while(front<rear) {
//队头结点出队
p = DeQueue(a);
displayNode(p);
//将队头结点的左右孩子依次入队
if (p->LeftChild!=NULL) {
EnQueue(a, p->LeftChild);
}
if (p->RightChild!=NULL) {
EnQueue(a, p->RightChild);
}
}
}
2.3.4 左右子树互换
void ExchangeLRChild(BiTree T) //互换左右子树
{
BiTree temp;
if (T == NULL)
return;
temp = T->LeftChild;
T->LeftChild = T->RightChild;
T->RightChild = temp;
ExchangeLRChild (T->LeftChild);
ExchangeLRChild (T->RightChild);
}
2.3.5 高度
int TreeHeight(BiTree T) //二叉树高度
{
int height, lheight, rheight;
if (T == NULL)
return 0;
lheight = TreeHeight (T->LeftChild);
rheight = TreeHeight (T->RightChild);
return (lheight > rheight ? lheight : rheight) + 1;
}
2.3.6 销毁二叉树
void destory_tree(BiTree T) //销毁二叉树
{
if(T == NULL)
return ;
destory_tree(T->LeftChild);
destory_tree(T->RightChild);
free(T);
}
2.3.7 测试
int main(void)
{
BiTree tree = PrecreatTree();
printf ("前序遍历:\n");
PreOrderTraverse(tree);
printf ("\n中序遍历:\n");
InOrderTraverse(tree);
printf ("\n后序遍历:\n");
PostOrderTraverse(tree);
printf ("\n层序遍历:\n");
LevelOdTraversal(tree);
printf ("\n二叉树高度: %d\n", TreeHeight(tree));
printf ("\n左右子树互换\n");
ExchangeLRChild(tree);
printf ("前序遍历:\n");
PreOrderTraverse(tree);
printf ("\n中序遍历:\n");
InOrderTraverse(tree);
printf ("\n后序遍历:\n");
PostOrderTraverse(tree);
printf ("\n二叉树高度: %d\n", TreeHeight(tree));
destory_tree(tree);
return 0;
}
2.4 二叉查找树 - 转载
来源:
https://www.cnblogs.com/skywang12345/p/3576328.html
2.4.1 定义
二叉查找树(Binary Search Tree),又被称为二叉搜索树。设x为二叉查找树中的一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。
2.4.2 特征
- 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 任意节点的左、右子树也分别为二叉查找树。
- 没有键值相等的节点(no duplicate nodes)。
例:
2.4.3 二叉树查找实现
2.4.3.1 定义数据类型
typedef int Type;
typedef struct BSTreeNode{
Type key; // 关键字(键值)
struct BSTreeNode *left; // 左孩子
struct BSTreeNode *right; // 右孩子
struct BSTreeNode *parent; // 父结点
}Node, *BSTree;
2.4.3.2 生成和插入结点
/*
* 生成返回二叉树结点
* key 键值
* parent 父结点
* left 左孩子结点
* right 右孩子结点
*
* 返回: 生成结点
*/
Node *create_bstree_node(Type key, Node *parent, Node *left, Node *right)
{
Node *node = NULL;
if ((node = (Node *) malloc (sizeof(Node))) == NULL)
return NULL;
node->key = key;
node->left = left;
node->right = right;
node->parent = parent;
return node;
}
/*
* 新建结点key ,插入到二叉树
* tree 二叉树根结点
* key 插入结点的键值
*
* 返回: 根结点
*/
Node *insert_bstree(BSTree tree, Type key)
{
Node *node;
if ((node = create_bstree_node(key, NULL, NULL, NULL)) == NULL) //判断是否成功创建结点
return tree;
return bstree_insert(tree, node);
}
/*
* 将结点插入到二叉树
* tree 二叉树根结点
* node 插入的结点
* 返回: 根结点
*/
Node *bstree_insert(BSTree tree, Node *node)
{
Node *x = tree;
Node *y = NULL;
while (x != NULL) {
y = x;
if (node->key < x->key)
x = x->left;
else
x = x->right;
}
node->parent = y;
if (y == NULL)
tree = node;
else if (node->key < y->key)
y->left = node;
else
y->right = node;
return tree;
}
2.4.3.3 遍历二叉树
void PreOrderTraverse(BSTree T)//二叉树的先序遍历
{
if(T == NULL)
return ;
printf("%d ",T->key);
PreOrderTraverse(T->left);
PreOrderTraverse(T->right);
}
void InOrderTraverse(BSTree T)//二叉树的中序遍历
{
if(T == NULL)
return ;
InOrderTraverse(T->left);
printf("%d ",T->key);
InOrderTraverse(T->right);
}
void PostOrderTraverse(BSTree T)//后序遍历
{
if(T==NULL)
return;
PostOrderTraverse(T->left);
PostOrderTraverse(T->right);
printf("%d ",T->key);
}
2.4.3.4 打印二叉树
/*
* 打印二叉树
* tree 二叉树结点
* key 结点键值
* direction 0 表示根结点
* -1 表示父节点的左孩子
* 1 表示父节点的右孩子
*
*/
void print_bstree(BSTree tree, Type key, int direction)
{
if (tree != NULL) {
if (direction == 0)
printf("%2d is root\n", tree->key);
else
printf("%2d is key:%2d %6s child\n", tree->key, key, direction == 1 ? "right" : "left");
print_bstree(tree->left, tree->key, -1);
print_bstree(tree->right,tree->key, 1);
}
}
2.4.3.5 查找结点
/*
* 递归实现
* 查找键值为key的结点
*/
Node *bstree_search (BSTree tree, Type key)
{
if (tree == NULL || tree->key == key)
return tree;
if (key < tree->key)
return bstree_search(tree->left, key);
else
return bstree_search(tree->right, key);
}
/*
* 非递归实现
* 查找键值为key的结点
*/
Node* iterative_bstree_search(BSTree tree, Type key)
{
while ((tree != NULL) && (tree->key != key)) {
if (key < tree->key)
tree = tree->left;
else
tree = tree->right;
}
return tree;
}
/*
* 查找最大结点:返回tree为根结点的二叉树的最大结点。
*/
Node *bstree_maximum(BSTree tree)
{
if (tree == NULL)
return NULL;
while(tree->right != NULL)
tree = tree->right;
return tree;
}
/*
* 查找最小结点:返回tree为根结点的二叉树的最小结点。
*/
Node *bstree_minimum(BSTree tree)
{
if (tree == NULL)
return NULL;
while(tree->left != NULL)
tree = tree->left;
return tree;
}
/*
* 找结点(node)的后继结点。即,查找"二叉树中数据值大于该结点"的"最小结点"。
*/
Node *bstree_successor (Node *node)
{
// 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
if (node->right != NULL)
return bstree_minimum (node->right);
// 如果node没有右孩子结点,则有以下两种可能:
// 1 node 是 “一个左孩子”,则“node”的后继结点为 “它的父节点”
// 2 node 是 “一个右孩子”,则查找“node”的最低的父节点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
Node* tmp = node->parent;
while ((tmp != NULL) && (node == tmp->right)) {
node = tmp;
tmp = tmp->parent;
}
return tmp;
}
/*
* 找结点(x)的前驱结点。即,查找"二叉树中数据值小于该结点"的"最大结点"。
*/
Node* bstree_predecessor(Node *node)
{
// 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
if (node->left != NULL)
return bstree_maximum(node->left);
// 如果node没有左孩子结点,则有以下两种可能:
// 1 node 是 “一个右孩子”,则“node”的前驱结点为 “它的父节点”
// 2 node 是 “一个左孩子”,则查找“node”的最低的父节点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
Node* tmp = node->parent;
while ((tmp != NULL) && (node == tmp->left)) {
node = tmp;
tmp = tmp->parent;
}
return tmp;
}
2.4.3.6 删除结点
/*
* 删除结点(node),并返回根节点
*
* tree 二叉树的根节点
* node 删除的结点
*/
Node* bstree_delete(BSTree tree, Node *node)
{
Node *x = NULL;
Node *y = NULL;
if ((node->left == NULL) || (node->right == NULL))
y = node;
else
y = bstree_successor (node);
if (y->left != NULL)
x = y->left;
else
x = y->right;
if (x != NULL)
x->parent = y->parent;
if (y->parent == NULL)
tree = x;
else if (y == y->parent->left)
y->parent->left = x;
else
y->parent->right = x;
if (y != node)
node->key = y->key;
if (y!=NULL)
free(y);
return tree;
}
/*
* 删除结点(key为节点的键值),并返回根节点
*
* tree 二叉树的根结点
* node 删除的结点
*
*/
Node* delete_bstree(BSTree tree, Type key)
{
Node *node;
if ((node = bstree_search(tree, key)) != NULL)
tree = bstree_delete(tree, node);
return tree;
}
2.4.3.7 销毁二叉树
/*
* 销毁二叉树
*/
void destroy_bstree(BSTree tree)
{
if (tree==NULL)
return ;
if (tree->left != NULL)
destroy_bstree(tree->left);
if (tree->right != NULL)
destroy_bstree(tree->right);
free(tree);
}
2.4.3.8 测试
#define ARR_SIZE(a) (sizeof(a) / sizeof(a[0]))
int main()
{
int i;
int arr[] = {5, 3, 1, 4, 10, 6, 2, 8, 7, 9};
BSTree root = NULL;
printf ("插入结点:\n");
for (i = 0; i < ARR_SIZE(arr); i++) {
printf ("%d ",arr[i]);
root = insert_bstree (root, arr[i]);
}
printf ("\n先序遍历:\n");
PreOrderTraverse(root);
printf ("\n中序遍历:\n");
InOrderTraverse(root);
printf ("\n后序遍历:\n");
PostOrderTraverse(root);
printf ("\n打印二叉树:\n");
print_bstree(root, root->key, 0);
printf ("最大键值: %d\n",bstree_maximum(root)->key);
printf ("最小键值: %d\n",bstree_minimum(root)->key);
printf ("递归查找找到key值为4的结点:%d\n",bstree_search (root, 4)->key);
printf ("非递归查找找到key值为4的结点:%d\n",iterative_bstree_search (root, 4)->key);
printf ("找到key值为4 的前驱结点key为:%d\n", bstree_predecessor(bstree_search (root, 4))->key);
printf ("找到key值为4 的后继结点key为:%d\n", bstree_successor(bstree_search (root, 4))->key);
printf ("删除key值为 4 的结点\n");
//bstree_delete (root, bstree_search (root, 4));
delete_bstree(root, 4);
printf ("\n打印二叉树:\n");
print_bstree(root, root->key, 0);
printf ("销毁二叉树\n");
destroy_bstree(root);
return 0;
}
2.5 平衡二叉查找树 - 转载
来源:
https://www.cnblogs.com/skywang12345/p/3576969.html#a1
2.5.1 定义
平衡二叉查找树(self-balancing binary search tree)是由苏联数学家Adelson-Velskii and Landis发明的一种自平衡二叉树(Balanced binary tree),称为AVL树。
2.5.2 特征
- 一个是继承自二叉查找树的查找属性(binary search property)
- 另一个是AVL树特有的平衡因子属性(balance factor property)。
- 本身首先是一棵二叉查找树
- 左右子树深度之差的绝对值不超过1;
- 左右子树仍然为平衡二叉树.
平衡因子BF = 左子树深度-右子树深度.
平衡二叉树每个结点的平衡因子只能是1,0,-1。若其绝对值超过1,则该二叉排序树就是不平衡的。
2.5.3 AVL树实现
2.5.3.1 定义数据类型
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
#define HEIGHT(p) ( (p == NULL) ? 0 : (( (Node *)(p))->height) )
typedef int Type;
typedef struct AVLTreeNode{
Type key; // 关键字(键值)
int height; // 高度
struct AVLTreeNode *left; // 左孩子
struct AVLTreeNode *right; // 右孩子
}Node, *AVLTree;
2.5.3.2 旋转
LL型平衡旋转法
以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
z
x /
/ \ --(左旋)--> x
y z /
y
例:
由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。
/*
* LL:左左对应的情况(左单旋转)。
*
* 返回值:旋转后的根节点
*/
Node* left_left_rotation(AVLTree k2)
{
AVLTree k1;
k1 = k2->left;
k2->left = k1->right;
k1->right = k2;
k2->height = MAX( HEIGHT(k2->left), HEIGHT(k2->right)) + 1;
k1->height = MAX( HEIGHT(k1->left), k2->height) + 1;
return k1;
}
RR型平衡旋转法
以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
y
x \
/ \ --(右旋)--> x
y z \
z
例:
由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。
/*
* RR:右右对应的情况(右单旋转)。
*
* 返回值:旋转后的根节点
*/
Node* right_right_rotation(AVLTree k1)
{
AVLTree k2;
k2 = k1->right;
k1->right = k2->left;
k2->left = k1;
k1->height = MAX( HEIGHT(k1->left), HEIGHT(k1->right)) + 1;
k2->height = MAX( HEIGHT(k2->right), k1->height) + 1;
return k2;
}
LR型平衡旋转法
例:
由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。
/*
* LR:左右对应的情况(左双旋转)。
*
* 返回值:旋转后的根节点
*/
Node* left_right_rotation(AVLTree k3)
{
k3->left = right_right_rotation(k3->left);
return left_left_rotation(k3);
}
RL型平衡旋转法
例:
/*
* RL:右左对应的情况(右双旋转)。
*
* 返回值:旋转后的根节点
*/
Node* right_left_rotation(AVLTree k1)
{
k1->right = left_left_rotation(k1->right);
return right_right_rotation(k1);
}
2.5.3.3 生成和插入结点
/*
* 创建AVL树结点。
*
* key 是键值。
* left 是左孩子。
* right 是右孩子。
*/
Node *avltree_create_node(Type key, Node *left, Node* right)
{
Node *node = NULL;
if ((node = (Node *)malloc(sizeof(Node))) == NULL)
return NULL;
node->key = key;
node->height = 0;
node->left = left;
node->right = right;
return node;
}
/*
* 将结点插入到AVL树中,并返回根节点
*
* tree AVL树的根结点
* key 插入的结点的键值
*
*/
Node *avltree_insert(AVLTree tree, Type key)
{
if (tree == NULL) {
// 新建节点
tree = avltree_create_node(key, NULL, NULL);
if (tree==NULL) {
printf("ERROR: create avltree node failed!\n");
return NULL;
}
} else if (key < tree->key) { // 应该将key插入到"tree的左子树"的情况
tree->left = avltree_insert(tree->left, key);
// 插入节点后,若AVL树失去平衡,则进行相应的调节。
if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2) {
if (key < tree->left->key)
tree = left_left_rotation(tree);
else
tree = left_right_rotation(tree);
}
} else if (key > tree->key) { // 应该将key插入到"tree的右子树"的情况
tree->right = avltree_insert(tree->right, key);
// 插入节点后,若AVL树失去平衡,则进行相应的调节。
if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2) {
if (key > tree->right->key)
tree = right_right_rotation(tree);
else
tree = right_left_rotation(tree);
}
} else {//key == tree->key)
printf("添加失败:不允许添加相同的节点!\n");
}
tree->height = MAX( HEIGHT(tree->left), HEIGHT(tree->right)) + 1;
return tree;
}
2.5.3.4 获取树高度
/*
* 获取AVL树的高度
*/
int avltree_height(AVLTree tree)
{
return HEIGHT(tree);
}
2.5.3.5 打印
/*
* 打印"AVL树"
*
* tree -- AVL树的节点
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
void print_avltree(AVLTree tree, Type key, int direction)
{
if(tree != NULL) {
if(direction==0) // tree是根节点
printf("%2d is root\n", tree->key);
else // tree是分支节点
printf("%2d is %2d's %6s child\n", tree->key, key, direction==1?"right" : "left");
print_avltree(tree->left, tree->key, -1);
print_avltree(tree->right,tree->key, 1);
}
}
2.5.3.6 遍历
/*
* 前序遍历"AVL树"
*/
void preorder_avltree(AVLTree tree)
{
if(tree != NULL) {
printf("%d ", tree->key);
preorder_avltree(tree->left);
preorder_avltree(tree->right);
}
}
/*
* 中序遍历"AVL树"
*/
void inorder_avltree(AVLTree tree)
{
if(tree != NULL) {
inorder_avltree(tree->left);
printf("%d ", tree->key);
inorder_avltree(tree->right);
}
}
/*
* 后序遍历"AVL树"
*/
void postorder_avltree(AVLTree tree)
{
if(tree != NULL) {
postorder_avltree(tree->left);
postorder_avltree(tree->right);
printf("%d ", tree->key);
}
}
2.5.3.7 查找
/*
* (递归实现)查找"AVL树x"中键值为key的节点
*/
Node *avltree_search(AVLTree x, Type key)
{
if (x==NULL || x->key==key)
return x;
if (key < x->key)
return avltree_search(x->left, key);
else
return avltree_search(x->right, key);
}
/*
* (非递归实现)查找"AVL树x"中键值为key的节点
*/
Node *iterative_avltree_search(AVLTree x, Type key)
{
while ((x!=NULL) && (x->key!=key)) {
if (key < x->key)
x = x->left;
else
x = x->right;
}
return x;
}
/*
* 查找最小结点:返回tree为根结点的AVL树的最小结点。
*/
Node *avltree_minimum(AVLTree tree)
{
if (tree == NULL)
return NULL;
while(tree->left != NULL)
tree = tree->left;
return tree;
}
/*
* 查找最大结点:返回tree为根结点的AVL树的最大结点。
*/
Node *avltree_maximum(AVLTree tree)
{
if (tree == NULL)
return NULL;
while(tree->right != NULL)
tree = tree->right;
return tree;
}
2.5.3.8 删除结点
/*
* 删除结点(z),返回根节点
*
* tree AVL树的根结点
* z 待删除的结点
*
*/
Node *delete_node(AVLTree tree, Node *z)
{
// 根为空 或者 没有要删除的节点,直接返回NULL。
if (tree==NULL || z==NULL)
return NULL;
if (z->key < tree->key) { // 待删除的节点在"tree的左子树"中
tree->left = delete_node(tree->left, z);
// 删除节点后,若AVL树失去平衡,则进行相应的调节。
if (HEIGHT(tree->right) - HEIGHT(tree->left) == 2) {
Node *r = tree->right;
if (HEIGHT(r->left) > HEIGHT(r->right))
tree = right_left_rotation(tree);
else
tree = right_right_rotation(tree);
}
} else if (z->key > tree->key) {// 待删除的节点在"tree的右子树"中
tree->right = delete_node(tree->right, z);
// 删除节点后,若AVL树失去平衡,则进行相应的调节。
if (HEIGHT(tree->left) - HEIGHT(tree->right) == 2) {
Node *l = tree->left;
if (HEIGHT(l->right) > HEIGHT(l->left))
tree = left_right_rotation(tree);
else
tree = left_left_rotation(tree);
}
} else { // tree是对应要删除的节点。
// tree的左右孩子都非空
if ((tree->left) && (tree->right)) {
if (HEIGHT(tree->left) > HEIGHT(tree->right)) {
// 如果tree的左子树比右子树高;
// 则(01)找出tree的左子树中的最大节点
// (02)将该最大节点的值赋值给tree。
// (03)删除该最大节点。
// 这类似于用"tree的左子树中最大节点"做"tree"的替身;
// 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。
Node *max = avltree_maximum(tree->left);
tree->key = max->key;
tree->left = delete_node(tree->left, max);
} else {
// 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1)
// 则(01)找出tree的右子树中的最小节点
// (02)将该最小节点的值赋值给tree。
// (03)删除该最小节点。
// 这类似于用"tree的右子树中最小节点"做"tree"的替身;
// 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。
Node *min = avltree_maximum(tree->right);
tree->key = min->key;
tree->right = delete_node(tree->right, min);
}
} else {
Node *tmp = tree;
tree = tree->left ? tree->left : tree->right;
free(tmp);
}
}
return tree;
}
/*
* 删除结点(key是节点值),返回根节点
*
* 参数说明:
* tree AVL树的根结点
* key 待删除的结点的键值
* 返回值:
* 根节点
*/
Node *avltree_delete(AVLTree tree, Type key)
{
Node *z;
if ((z = avltree_search(tree, key)) != NULL)
tree = delete_node(tree, z);
return tree;
}
2.5.3.9 销毁树
/*
* 销毁AVL树
*/
void destroy_avltree(AVLTree tree)
{
if (tree==NULL)
return ;
if (tree->left != NULL)
destroy_avltree(tree->left);
if (tree->right != NULL)
destroy_avltree(tree->right);
free(tree);
}
2.5.3.10 测试
#define ARR_SIZE(a) (sizeof(a) / sizeof(a[0]))
int main()
{
int i, Len;
AVLTree root = NULL;
int arr[]= {3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9};
printf("高度: %d\n", avltree_height(root));
printf("依次添加: ");
Len = ARR_SIZE (arr);
for (i = 0; i < Len; i++) {
printf ("%d ",arr[i]);
root = avltree_insert(root, arr[i]);
}
printf ("\n前序遍历\n");
preorder_avltree (root);
printf ("\n中序遍历\n");
inorder_avltree (root);
printf ("\n后序遍历\n");
postorder_avltree (root);
printf("\n高度: %d\n", avltree_height(root));
printf("打印二叉树: \n");
print_avltree(root, root->key, 0);
printf ("非递归查找 key值为16的结点: %d\n",iterative_avltree_search(root, 16)->key);
printf ("递归查找 key值为16的结点: %d\n",avltree_search(root, 16)->key);
printf ("最小值: %d\n", avltree_minimum(root)->key);
printf ("最大值: %d\n", avltree_maximum(root)->key);
printf("\n删除根节点: 8");
root = avltree_delete(root, 8);
printf("\n高度: %d\n", avltree_height(root));
printf("打印二叉树: \n");
print_avltree(root, root->key, 0);
printf ("销毁二叉树\n");
destroy_avltree(root);
return 0;
}
2.6 红黑树 - 转载
来源:
https://www.cnblogs.com/skywang12345/p/3624177.html
https://www.cnblogs.com/skywang12345/p/3245399.html
参考:
https://www.jianshu.com/p/e136ec79235c
2.6.1 红黑树定义和性质
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面性质:
- 性质1:每个节点要么是黑色,要么是红色。
- 性质2:根节点是黑色。
- 性质3:每个叶子节点(NIL)是黑色。
- 性质4:每个红色结点的两个子结点一定都是黑色。
- 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
从性质5又可以推出:如果一个结点存在黑子结点,那么该结点肯定有两个子结点
##2.6.2 实现
2.6.2.1 定义
#define rb_parent(r) ((r)->parent)
#define rb_color(r) ((r)->color)
#define rb_is_red(r) ((r)->color==RED)
#define rb_is_black(r) ((r)->color==BLACK)
#define rb_set_black(r) do { (r)->color = BLACK; } while (0)
#define rb_set_red(r) do { (r)->color = RED; } while (0)
#define rb_set_parent(r,p) do { (r)->parent = (p); } while (0)
#define rb_set_color(r,c) do { (r)->color = (c); } while (0)
#define RED 0 // 红色节点
#define BLACK 1 // 黑色节点
typedef int Type;
// 红黑树的节点
typedef struct RBTreeNode{
unsigned char color; // 颜色(RED 或 BLACK)
Type key; // 关键字(键值)
struct RBTreeNode *left; // 左孩子
struct RBTreeNode *right; // 右孩子
struct RBTreeNode *parent; // 父结点
}Node, *RBTree;
// 红黑树的根
typedef struct rb_root{
Node *node;
}RBRoot;
2.6.2.2 创建
/*
* 创建红黑树,返回"红黑树的根"!
*/
RBRoot* create_rbtree()
{
RBRoot *root = (RBRoot *)malloc(sizeof(RBRoot));
root->node = NULL;
return root;
}
/*
* 创建结点
*
* 参数说明:
* key 是键值。
* parent 是父结点。
* left 是左孩子。
* right 是右孩子。
*/
static Node* create_rbtree_node(Type key, Node *parent, Node *left, Node* right)
{
Node* p;
if ((p = (Node *)malloc(sizeof(Node))) == NULL)
return NULL;
p->key = key;
p->left = left;
p->right = right;
p->parent = parent;
p->color = BLACK; // 默认为黑色
return p;
}
2.6.2.3 遍历
/*
* 前序遍历"红黑树"
*/
static void preorder(RBTree tree)
{
if(tree != NULL) {
printf("%d ", tree->key);
preorder(tree->left);
preorder(tree->right);
}
}
void preorder_rbtree(RBRoot *root)
{
if (root)
preorder(root->node);
}
/*
* 中序遍历"红黑树"
*/
static void inorder(RBTree tree)
{
if (tree != NULL) {
inorder(tree->left);
printf("%d ", tree->key);
inorder(tree->right);
}
}
void inorder_rbtree(RBRoot *root)
{
if (root)
inorder(root->node);
}
/*
* 后序遍历"红黑树"
*/
static void postorder(RBTree tree)
{
if(tree != NULL) {
postorder(tree->left);
postorder(tree->right);
printf("%d ", tree->key);
}
}
void postorder_rbtree(RBRoot *root)
{
if (root)
postorder(root->node);
}
2.6.2.4 查找
/*
* (递归实现)查找"红黑树x"中键值为key的节点
*/
static Node* search(RBTree x, Type key)
{
if (x==NULL || x->key==key)
return x;
if (key < x->key)
return search(x->left, key);
else
return search(x->right, key);
}
int rbtree_search(RBRoot *root, Type key)
{
if (root)
return search(root->node, key)? 0 : -1;
}
/*
* (非递归实现)查找"红黑树x"中键值为key的节点
*/
static Node* iterative_search(RBTree x, Type key)
{
while ((x!=NULL) && (x->key!=key)) {
if (key < x->key)
x = x->left;
else
x = x->right;
}
return x;
}
int iterative_rbtree_search(RBRoot *root, Type key)
{
if (root)
return iterative_search(root->node, key) ? 0 : -1;
}
/*
* 查找最小结点:返回tree为根结点的红黑树的最小结点。
*/
static Node* minimum(RBTree tree)
{
if (tree == NULL)
return NULL;
while(tree->left != NULL)
tree = tree->left;
return tree;
}
int rbtree_minimum(RBRoot *root, int *val)
{
Node *node;
if (root)
node = minimum(root->node);
if (node == NULL)
return -1;
*val = node->key;
return 0;
}
/*
* 查找最大结点:返回tree为根结点的红黑树的最大结点。
*/
static Node* maximum(RBTree tree)
{
if (tree == NULL)
return NULL;
while(tree->right != NULL)
tree = tree->right;
return tree;
}
int rbtree_maximum(RBRoot *root, int *val)
{
Node *node;
if (root)
node = maximum(root->node);
if (node == NULL)
return -1;
*val = node->key;
return 0;
}
/*
* 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。
*/
static Node* rbtree_successor(RBTree x)
{
// 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
if (x->right != NULL)
return minimum(x->right);
// 如果x没有右孩子。则x有以下两种可能:
// (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
// (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
Node* y = x->parent;
while ((y!=NULL) && (x==y->right)) {
x = y;
y = y->parent;
}
return y;
}
/*
* 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。
*/
static Node* rbtree_predecessor(RBTree x)
{
// 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
if (x->left != NULL)
return maximum(x->left);
// 如果x没有左孩子。则x有以下两种可能:
// (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
// (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
Node* y = x->parent;
while ((y!=NULL) && (x==y->left)) {
x = y;
y = y->parent;
}
return y;
}
2.6.2.5 旋转
/*
* 对红黑树的节点(x)进行左旋转
*
* 左旋示意图(对节点x进行左旋):
* px px
* / /
* x y
* / \ --(左旋)--> / \ #
* lx y x ry
* / \ / \
* ly ry lx ly
*
*
*/
static void rbtree_left_rotate(RBRoot *root, Node *x)
{
// 设置x的右孩子为y
Node *y = x->right;
// 将 “y的左孩子” 设为 “x的右孩子”;
// 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
x->right = y->left;
if (y->left != NULL)
y->left->parent = x;
// 将 “x的父亲” 设为 “y的父亲”
y->parent = x->parent;
if (x->parent == NULL) {
//tree = y; // 如果 “x的父亲” 是空节点,则将y设为根节点
root->node = y; // 如果 “x的父亲” 是空节点,则将y设为根节点
} else {
if (x->parent->left == x)
x->parent->left = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
else
x->parent->right = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
}
// 将 “x” 设为 “y的左孩子”
y->left = x;
// 将 “x的父节点” 设为 “y”
x->parent = y;
}
/*
* 对红黑树的节点(y)进行右旋转
*
* 右旋示意图(对节点y进行左旋):
* py py
* / /
* y x
* / \ --(右旋)--> / \ #
* x ry lx y
* / \ / \ #
* lx rx rx ry
*
*/
static void rbtree_right_rotate(RBRoot *root, Node *y)
{
// 设置x是当前节点的左孩子。
Node *x = y->left;
// 将 “x的右孩子” 设为 “y的左孩子”;
// 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
y->left = x->right;
if (x->right != NULL)
x->right->parent = y;
// 将 “y的父亲” 设为 “x的父亲”
x->parent = y->parent;
if (y->parent == NULL) {
//tree = x; // 如果 “y的父亲” 是空节点,则将x设为根节点
root->node = x; // 如果 “y的父亲” 是空节点,则将x设为根节点
} else {
if (y == y->parent->right)
y->parent->right = x; // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
else
y->parent->left = x; // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
}
// 将 “y” 设为 “x的右孩子”
x->right = y;
// 将 “y的父节点” 设为 “x”
y->parent = x;
}
2.6.2.6 二叉修正
/*
* 红黑树插入修正函数
*
* 在向红黑树中插入节点之后(失去平衡),再调用该函数;
* 目的是将它重新塑造成一颗红黑树。
*
* 参数说明:
* root 红黑树的根
* node 插入的结点 // 对应《算法导论》中的z
*/
static void rbtree_insert_fixup(RBRoot *root, Node *node)
{
Node *parent, *gparent;
// 若“父节点存在,并且父节点的颜色是红色”
while ((parent = rb_parent(node)) && rb_is_red(parent)) {
gparent = rb_parent(parent);
//若“父节点”是“祖父节点的左孩子”
if (parent == gparent->left) {
// Case 1条件:叔叔节点是红色
{
Node *uncle = gparent->right;
if (uncle && rb_is_red(uncle)) {
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
// Case 2条件:叔叔是黑色,且当前节点是右孩子
if (parent->right == node) {
Node *tmp;
rbtree_left_rotate(root, parent);
tmp = parent;
parent = node;
node = tmp;
}
// Case 3条件:叔叔是黑色,且当前节点是左孩子。
rb_set_black(parent);
rb_set_red(gparent);
rbtree_right_rotate(root, gparent);
} else {//若“z的父节点”是“z的祖父节点的右孩子”
// Case 1条件:叔叔节点是红色
{
Node *uncle = gparent->left;
if (uncle && rb_is_red(uncle)) {
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
// Case 2条件:叔叔是黑色,且当前节点是左孩子
if (parent->left == node) {
Node *tmp;
rbtree_right_rotate(root, parent);
tmp = parent;
parent = node;
node = tmp;
}
// Case 3条件:叔叔是黑色,且当前节点是右孩子。
rb_set_black(parent);
rb_set_red(gparent);
rbtree_left_rotate(root, gparent);
}
}
// 将根节点设为黑色
rb_set_black(root->node);
}
2.6.2.7 插入
/*
* 添加节点:将节点(node)插入到红黑树中
*
* 参数说明:
* root 红黑树的根
* node 插入的结点 // 对应《算法导论》中的z
*/
static void rbtree_insert(RBRoot *root, Node *node)
{
Node *y = NULL;
Node *x = root->node;
// 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。
while (x != NULL) {
y = x;
if (node->key < x->key)
x = x->left;
else
x = x->right;
}
rb_parent(node) = y;
if (y != NULL) {
if (node->key < y->key)
y->left = node; // 情况2:若“node所包含的值” < “y所包含的值”,则将node设为“y的左孩子”
else
y->right = node; // 情况3:(“node所包含的值” >= “y所包含的值”)将node设为“y的右孩子”
} else {
root->node = node; // 情况1:若y是空节点,则将node设为根
}
// 2. 设置节点的颜色为红色
node->color = RED;
// 3. 将它重新修正为一颗二叉查找树
rbtree_insert_fixup(root, node);
}
/*
* 新建结点(节点键值为key),并将其插入到红黑树中
*
* 参数说明:
* root 红黑树的根
* key 插入结点的键值
* 返回值:
* 0,插入成功
* -1,插入失败
*/
int insert_rbtree(RBRoot *root, Type key)
{
Node *node; // 新建结点
// 不允许插入相同键值的节点。
// (若想允许插入相同键值的节点,注释掉下面两句话即可!)
if (search(root->node, key) != NULL)
return -1;
// 如果新建结点失败,则返回。
if ((node=create_rbtree_node(key, NULL, NULL, NULL)) == NULL)
return -1;
rbtree_insert(root, node);
return 0;
}
2.6.2.8 删除
/*
* 红黑树删除修正函数
*
* 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数;
* 目的是将它重新塑造成一颗红黑树。
*
* 参数说明:
* root 红黑树的根
* node 待修正的节点
*/
static void rbtree_delete_fixup(RBRoot *root, Node *node, Node *parent)
{
Node *other;
while ((!node || rb_is_black(node)) && node != root->node)
{
if (parent->left == node) {
other = parent->right;
if (rb_is_red(other)) {
// Case 1: x的兄弟w是红色的
rb_set_black(other);
rb_set_red(parent);
rbtree_left_rotate(root, parent);
other = parent->right;
}
if ((!other->left || rb_is_black(other->left)) &&
(!other->right || rb_is_black(other->right))) {
// Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的
rb_set_red(other);
node = parent;
parent = rb_parent(node);
} else {
if (!other->right || rb_is_black(other->right)) {
// Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
rb_set_black(other->left);
rb_set_red(other);
rbtree_right_rotate(root, other);
other = parent->right;
}
// Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
rb_set_color(other, rb_color(parent));
rb_set_black(parent);
rb_set_black(other->right);
rbtree_left_rotate(root, parent);
node = root->node;
break;
}
} else {
other = parent->left;
if (rb_is_red(other)) {
// Case 1: x的兄弟w是红色的
rb_set_black(other);
rb_set_red(parent);
rbtree_right_rotate(root, parent);
other = parent->left;
}
if ((!other->left || rb_is_black(other->left)) &&
(!other->right || rb_is_black(other->right))) {
// Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的
rb_set_red(other);
node = parent;
parent = rb_parent(node);
} else {
if (!other->left || rb_is_black(other->left)) {
// Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。
rb_set_black(other->right);
rb_set_red(other);
rbtree_left_rotate(root, other);
other = parent->left;
}
// Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
rb_set_color(other, rb_color(parent));
rb_set_black(parent);
rb_set_black(other->left);
rbtree_right_rotate(root, parent);
node = root->node;
break;
}
}
}
if (node)
rb_set_black(node);
}
/*
* 删除结点
*
* 参数说明:
* tree 红黑树的根结点
* node 删除的结点
*/
void rbtree_delete(RBRoot *root, Node *node)
{
Node *child, *parent;
int color;
// 被删除节点的"左右孩子都不为空"的情况。
if ( (node->left!=NULL) && (node->right!=NULL) ) {
// 被删节点的后继节点。(称为"取代节点")
// 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。
Node *replace = node;
// 获取后继节点
replace = replace->right;
while (replace->left != NULL)
replace = replace->left;
// "node节点"不是根节点(只有根节点不存在父节点)
if (rb_parent(node))
{
if (rb_parent(node)->left == node)
rb_parent(node)->left = replace;
else
rb_parent(node)->right = replace;
} else
// "node节点"是根节点,更新根节点。
root->node = replace;
// child是"取代节点"的右孩子,也是需要"调整的节点"。
// "取代节点"肯定不存在左孩子!因为它是一个后继节点。
child = replace->right;
parent = rb_parent(replace);
// 保存"取代节点"的颜色
color = rb_color(replace);
// "被删除节点"是"它的后继节点的父节点"
if (parent == node) {
parent = replace;
} else {
// child不为空
if (child)
rb_set_parent(child, parent);
parent->left = child;
replace->right = node->right;
rb_set_parent(node->right, replace);
}
replace->parent = node->parent;
replace->color = node->color;
replace->left = node->left;
node->left->parent = replace;
if (color == BLACK)
rbtree_delete_fixup(root, child, parent);
free(node);
return ;
}
if (node->left !=NULL)
child = node->left;
else
child = node->right;
parent = node->parent;
// 保存"取代节点"的颜色
color = node->color;
if (child)
child->parent = parent;
// "node节点"不是根节点
if (parent) {
if (parent->left == node)
parent->left = child;
else
parent->right = child;
} else
root->node = child;
if (color == BLACK)
rbtree_delete_fixup(root, child, parent);
free(node);
}
/*
* 删除键值为key的结点
*
* 参数说明:
* tree 红黑树的根结点
* key 键值
*/
void delete_rbtree(RBRoot *root, Type key)
{
Node *z, *node;
if ((z = search(root->node, key)) != NULL)
rbtree_delete(root, z);
}
/*
* 销毁红黑树
*/
static void rbtree_destroy(RBTree tree)
{
if (tree==NULL)
return ;
if (tree->left != NULL)
rbtree_destroy(tree->left);
if (tree->right != NULL)
rbtree_destroy(tree->right);
free(tree);
}
void destroy_rbtree(RBRoot *root)
{
if (root != NULL)
rbtree_destroy(root->node);
free(root);
}
2.6.2.9 打印
/*
* 打印"红黑树"
*
* tree -- 红黑树的节点
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
static void rbtree_print(RBTree tree, Type key, int direction)
{
if(tree != NULL)
{
if(direction==0) // tree是根节点
printf("%2d(B) is root\n", tree->key);
else // tree是分支节点
printf("%2d(%s) is %2d's %6s child\n", tree->key, rb_is_red(tree)?"R":"B", key, direction==1?"right" : "left");
rbtree_print(tree->left, tree->key, -1);
rbtree_print(tree->right,tree->key, 1);
}
}
void print_rbtree(RBRoot *root)
{
if (root!=NULL && root->node!=NULL)
rbtree_print(root->node, root->node->key, 0);
}
2.6.2.10 测试
#define CHECK_INSERT 0 // "插入"动作的检测开关(0,关闭;1,打开)
#define CHECK_DELETE 0 // "删除"动作的检测开关(0,关闭;1,打开)
#define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) )
void main()
{
int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};
int i, ilen=LENGTH(a);
RBRoot *root=NULL;
root = create_rbtree();
printf("== 原始数据: ");
for(i=0; i<ilen; i++)
printf("%d ", a[i]);
printf("\n");
for(i=0; i<ilen; i++) {
insert_rbtree(root, a[i]);
#if CHECK_INSERT
printf("== 添加节点: %d\n", a[i]);
printf("== 树的详细信息: \n");
print_rbtree(root);
printf("\n");
#endif
}
printf("== 前序遍历: ");
preorder_rbtree(root);
printf("\n== 中序遍历: ");
inorder_rbtree(root);
printf("\n== 后序遍历: ");
postorder_rbtree(root);
printf("\n");
if (rbtree_minimum(root, &i)==0)
printf("== 最小值: %d\n", i);
if (rbtree_maximum(root, &i)==0)
printf("== 最大值: %d\n", i);
printf("== 树的详细信息: \n");
print_rbtree(root);
printf("\n");
#if CHECK_DELETE
for(i=0; i<ilen; i++) {
delete_rbtree(root, a[i]);
printf("== 删除节点: %d\n", a[i]);
if (root)
{
printf("== 树的详细信息: \n");
print_rbtree(root);
printf("\n");
}
}
#endif
destroy_rbtree(root);
}
2.7 B树相关
https://www.zhihu.com/question/57466414
https://www.jianshu.com/p/ace3cd6526c4
2.8 哈夫曼树
2.8.1 相关定义
-
路径:在一棵树中,一个结点到另一个结点之间的通路,称为路径。上图中,从根结点e到叶子结点c的路径为 e -> f -> g -> c
-
路径长度:在一条路径中,每经过一个结点,路径长度都要加 1 。例如在一棵树中,规定根结点所在层数为1层,那么从根结点到第 i 层结点的路径长度为 i - 1 。上图中,从根结点e到叶子结点 c 的路径长度为 3。
-
结点的权:给每一个结点赋予一个新的数值,被称为这个结点的权。上图中,结点 a 的权为 7,结点 b 的权为 5。
-
结点的带权路径长度:指的是从根结点到该结点之间的路径长度与该结点的权的乘积。上图中,结点 b 的带权路径长度为 2 * 5 = 10 。
-
树带权路径长度:树的带权路径长度为树中所有叶子结点的带权路径长度之和。通常记作 “WPL” 。上图所示的这颗树的带权路径长度为: WPL = 7 * 1 + 5 * 2 + 2 * 3 + 4 * 3
哈夫曼树定义: 给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,则称该二叉树为哈夫曼树,也被称为最优二叉树。
在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。
2.8.2 实现
对于给定的有各自权值的 n 个结点,构建哈夫曼树有一个行之有效的办法:
- 在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;
- 在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;
- 重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。
代码来源:
https://www.cnblogs.com/zhangming-blog/p/5395950.html
2.8.2.1 定义数据结构
#define LENGTH 6
typedef int ElemType;
typedef struct HuffmanTreeNode{
ElemType data; //哈夫曼树中节点的权值
struct HuffmanTreeNode* left;
struct HuffmanTreeNode* right;
}HuffmanTreeNode,*PtrHuffman;
2.8.2.2 创建哈夫曼树
/**
* 创建哈夫曼树
*/
PtrHuffman createHuffmanTree(ElemType arr[])
{
int i,j;
PtrHuffman ptrArr[LENGTH];
PtrHuffman ptr,pRoot=NULL;
for (int i = 0; i < LENGTH; i++){ //初始化结构体指针数组,数组中每一个元素为一个结构体指针类型
ptr = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));
ptr->data = arr[i];
ptr->left = ptr->right = NULL;
ptrArr[i] = ptr;
}
for(i = 1; i < LENGTH; i++){ //进行 n-1 次循环建立哈夫曼树
//k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标
int k1 = -1, k2;
for(int j = 0; j < LENGTH; j++){
if (ptrArr[j] != NULL && k1 == -1){
k1 = j;
continue;
}
if (ptrArr[j] != NULL){
k2 = j;
break;
}
}
//将指针数组中的指针指向的最小值赋值给索引号为k1的,次小值赋值给索引号为k2的
for (j = k2; j < LENGTH; j++){
if(ptrArr[j] != NULL){
if(ptrArr[j]->data < ptrArr[k1]->data){
k2 = k1;
k1 = j;
}else if(ptrArr[j]->data < ptrArr[k2]->data){
k2 = j;
}
}
}
//由最小权值树和次最小权值树建立一棵新树,pRoot指向树根结点
pRoot = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));
pRoot->data = ptrArr[k1]->data + ptrArr[k2]->data;
pRoot->left = ptrArr[k1];
pRoot->right = ptrArr[k2];
ptrArr[k1] = pRoot; //将指向新树的指针赋给ptrArr指针数组中k1位置
ptrArr[k2] = NULL; //k2位置为空
}
return pRoot;
}
2.8.2.3 计算WPL
/**
* 计算哈夫曼树带权路径长度WPL
*/
ElemType calculateWeightLength(PtrHuffman ptrTree,int len)
{
if(ptrTree==NULL){ //空树返回0
return 0;
}else{
if(ptrTree->left==NULL && ptrTree->right==NULL){ //访问到叶子节点
return ptrTree->data * len;
}else{
return calculateWeightLength(ptrTree->left,len+1) + calculateWeightLength(ptrTree->right,len+1); //向下递归计算
}
}
}
2.8.2.4 打印
/**
* 哈夫曼树编码(叶子节点按中序方式依次打印其编码)
*/
void HuffmanCoding(PtrHuffman ptrTree,int len){
//静态局部变量相当于全局变量(只是只有在这个函数中能访问,但是生命周期是和全局变量差不多的)函数退出之后变量还在,而且只在第一次进入的时候做初始化,以后会跳过初始化语句,保留原来的值
static int arr[20];
if(ptrTree != NULL){
if(ptrTree->left==NULL && ptrTree->right==NULL){
printf("结点权值为%d的编码: ", ptrTree->data);
for(int i = 0; i < len; i++){
printf("%d", arr[i]);
}
printf("\n");
}else{
arr[len] = 0;
HuffmanCoding(ptrTree->left,len+1);
arr[len] = 1;
HuffmanCoding(ptrTree->right,len+1);
}
}
}
/**
* 打印哈夫曼树中各个节点的孩子节点
* 若为叶子节点,则只显示提示信息
* @param node 需要显示孩子节点的父节点
*/
void printHuffmanTreeChildNode(PtrHuffman node)
{
if(node->left == NULL && node->right == NULL){
printf("x=%d是哈夫曼树中的叶子节点",node->data);
printf("\n\n");
return;
}
if(node->left != NULL){
printf("x=%d在哈夫曼树中的左孩子节点是lchild=%d",node->data,node->left->data);
printf("\n");
}
if(node->right != NULL){
printf("x=%d在哈夫曼树中的右孩子节点是rchild=%d",node->data,node->right->data);
printf("\n");
}
printf("\n");
}
/**
* 中序打印哈夫曼树的节点
*/
void midOrderprintHuffmanTreeNode(PtrHuffman pRoot)
{
if(pRoot==NULL){
return;
}else{
midOrderprintHuffmanTreeNode(pRoot->left);
printf("%d ",pRoot->data);
midOrderprintHuffmanTreeNode(pRoot->right);
}
}
/**
* 先序打印哈夫曼树的节点
*/
void PreOrderprintHuffmanTreeNode(PtrHuffman pRoot)
{
if(pRoot==NULL){
return;
}else{
printHuffmanTreeChildNode(pRoot); //依次打印哈夫曼树中各个节点的孩子节点
PreOrderprintHuffmanTreeNode(pRoot->left);
PreOrderprintHuffmanTreeNode(pRoot->right);
}
}
2.8.2.5 测试
int main(){
ElemType arr[] = {3,9,5,12,6,15};
PtrHuffman pRoot = createHuffmanTree(arr); //返回指向哈夫曼树根节点的指针
printf("==========中序打印哈夫曼树节点数据==========\n");
midOrderprintHuffmanTreeNode(pRoot);
printf("\n\n");
printf("==========先序打印哈夫曼树节点关系==========\n");
PreOrderprintHuffmanTreeNode(pRoot);
printf("==========计算带权路径长度==========\n");
printf("WeightLength=%d\n",calculateWeightLength(pRoot,0));
printf("\n");
printf("==========各节点的哈夫曼树编码==========\n");
HuffmanCoding(pRoot,0);
fprintf(stdout,"\n");
return 0;
}