1. 树
非线性结构,层次结构
每个元素至多有一个前趋,但可以有多个后继;根结点没有前趋,其他结点有且只有一个前趋
数据元素间是一对多的关系
树、二叉树、森林
树是n(n>=0)个结点的有限集合,当n=0时为空树,否则称为非空树。
1. 树有且仅有一个根结点
2. 除根节点外,其余结点可分为m(m>=0)个互不相交的集合,每个集合本身又是一棵树,称为根的子树
空树:没有任何结点的树
最小非空树:只有一个根节点
结点:一个元素及指向其子树的分支
结点的度:结点拥有的子树数目(结点有几个子树分支)
叶子、终端结点:指度为0的结点,没有子树
分支结点、非终端结点:度不为0的结点;不为根的分支结点称为内部结点
树的度:指树中各结点度的最大值
结点的层次:从根结点开始,根为第一层,根的子树为第二层,依次类推
树的深度、高度:树中结点的最大层次数
有序树和无序树:如果将树中结点的各子树看成从左到右依次有序且不能交换,则称该树为有序树;否则称为无序树
森林:m棵互不相交的树的集合
1.1 树的基本运算:
置空setnull
求根root
求父节点parent
求孩子child
建树create(x,F),生成一个以x为根节点,以森林F为子树森林的树
求右兄弟rsibling
插入子树addchild
删除子树delchild
遍历操作traverse
1.2 二叉树
每个结点至多两个子树。
二叉树的数据结构:
struct TreeNode
{
TreeNode * lchild; // 左子树
TreeNode * rchild; // 右子树
void * data; // 数据
}
分类:
满二叉树:每层含有结点数为最大值,深度为k,且含有2^k - 1个结点的二叉树
完全二叉树:k-1层前含有结点数为最大值,k层不一定满,但全部集中在左边而空缺留在右边,子节点只可能在第k层和第k-1层出现;满二叉树一定是完全二叉树
二叉树的存储结构
顺序存储:
1. 完全二叉树可根据结点编号依次存储各结点
2. 非完全二叉树需要添加空结点使之成为完全二叉树;会造成空间浪费,最坏的情况是单右枝树(全是右结点)
链式存储:
双链法(二叉链表):
在内存中存储两个子节点的存储地址;方便从根往下找;查找父节点和祖先则不方便
三链法(三叉链表):
在二叉链表上增加存储父节点的地址
1.3 二叉树的遍历
非线性结构线性化
递归算法:
前序遍历
中序遍历
后序遍历
非递归算法:
前序遍历
中序遍历
后序遍历
层次遍历
遍历序列与二叉树的复原
由两种遍历序列确定一颗二叉树
二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一的前驱和后继。对于二叉树的结点,查找左右子女是方便的,其前驱后继只有在遍历中得到。为了容易找到前驱和后继,有两种方法:
1. 在结点结构中增加向前和向后的指针,这种方法增加了存储开销,不可取
2. 利用二叉树的空链指针(线索二叉树)
#include <stdio.h>
/**
* 构造一棵二叉树
* 15
* / \
* 6 18
* / \
* 3 7
* /
* 9
*
*
*/
typedef struct sTreeNode TreeNode;
typedef struct sTreeNode
{
TreeNode *lchild;
TreeNode *rchild;
int data;
} TreeNode, *PTreeNode;
// 前序遍历
void qianxu(TreeNode *root)
{
/**
* 跟结点为空 => 返回
* 根结点不为空 => 输出根结点 => 左子树为根结点前序遍历 => 右子树为根结点前序遍历
*
* 输出跟结点 => 递归输出左子树 => 递归输出右子树
*/
if (NULL == root)
{
return;
}
printf("%d\t", root->data);
qianxu(root->lchild);
qianxu(root->rchild);
}
// 后序遍历
void houxu(TreeNode *root)
{
/**
* 递归输出左子树 => 递归输出右子树 => 输出跟结点
*/
if (NULL == root)
{
return;
}
houxu(root->lchild);
houxu(root->rchild);
printf("%d\t", root->data);
}
// 中序遍历
void zhongxu(TreeNode *root)
{
/**
* 递归输出左子树 => 输出跟结点 => 递归输出右子树
*/
if (NULL == root)
{
return;
}
zhongxu(root->lchild);
printf("%d\t", root->data);
zhongxu(root->rchild);
}
int main()
{
TreeNode root, t1, t2, t3, t4, t5;
root.lchild = root.rchild = t1.lchild = t1.rchild = t2.rchild = t2.lchild = t3.lchild = t3.rchild = t4.lchild = t5.rchild = t4.rchild = t5.lchild = NULL;
root.data = 15;
t1.data = 6;
t2.data = 3;
t3.data = 7;
t4.data = 9;
t5.data = 18;
root.lchild = &t1;
root.rchild = &t5;
t1.lchild = &t2;
t1.rchild = &t3;
t3.lchild = &t4;
printf("\nqianxu\n");
qianxu(&root); // 输出 15 6 3 7 9 18
printf("\nzhongxu\n");
zhongxu(&root); // 输出 3 6 9 7 15 18
printf("\nhouxu\n");
houxu(&root); // 输出 3 9 7 6 18 15
}
1.4 线索二叉树(Threaded BinaryTree)
加上线索的二叉树称为线索二叉树。对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
线索二叉树中的线索能记录每个结点前驱和后继信息。为了区别线索指针和孩子指针,在每个结点中设置标志ltag和rtag。
当tag为0时,child是指向孩子的指针;否则child是指向结点的前驱或后继的线索。
线索二叉树的结点结构:
struct ThreadedBT
{
BYTE ltag; // 标记lchild的目标是左子树还是前驱
BYTE rtag; // 标记rtag的目标是右子树还是后继
ThreadedBT* lchild;
ThreadedBT* lchild;
void* data;
}
1.5 二叉搜索树/二叉查找树/二叉排序树(Binary Search Tree : BST)
它或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树。
对任何结点x,其左子树中的key最大不超过x.key;其右子树中的key最大不低于x.key。
二叉搜索树可以通过中序遍历将所有key有序输出。
#include <stdio.h>
#include <memory.h>
typedef struct sTreeNode TreeNode;
typedef struct sTreeNode
{
TreeNode *lchild;
TreeNode *rchild;
TreeNode *parent;
int data;
} TreeNode, *PTreeNode;
struct TreeNode *new ()
{
PTreeNode _t = (PTreeNode)malloc(sizeof(TreeNode));
_t->lchild = NULL;
_t->rchild = NULL;
_t->parent = NULL;
_t->data = 0;
return _t;
}
void dispose(TreeNode *_node)
{
free(_node);
}
// 查找
struct TreeNode *search(TreeNode *root, int key)
{
if (!root)
return NULL;
if (key == root->data)
return root;
if (root->data > key)
return search(root->lchild, key);
return search(root->rchild, key);
}
// 插入
struct TreeNode *insert(TreeNode *root, int key)
{
if (!root)
{
TreeNode *_n = new ();
_n->data = key;
return _n;
}
if (key == root->data)
{
TreeNode *_n = new ();
_n->data = key;
_n->lchild = root->lchild;
_n->parent = root;
root->lchild = _n;
return root;
}
if (root->data > key)
{
TreeNode *_n = insert(root->lchild, key);
root->lchild = _n;
return root;
}
else
{
TreeNode *_n = insert(root->rchild, key);
root->rchild = _n;
return root;
}
}
struct TreeNode *get_min_node(TreeNode *root)
{
if (root && root->lchild)
return get_min_node(root->lchild);
return root;
}
// 删除
struct TreeNode *delete (TreeNode *root, int key)
{
if (!root)
return NULL;
if (key == root->data)
{
if (!root->rchild) // 右结点为空
{
// 返回左节点
TreeNode *_n = root->lchild;
if (_n)
{
_n->parent = NULL;
}
dispose(root); // 释放root节点
return _n;
}
else
{
TreeNode *_n = get_min_node(root->rchild);
_n->parent->lchild = NULL;
_n->parent = NULL;
_n->lchild = root->lchild;
_n->rchild = root->rchild;
dispose(root); // 释放root节点
return _n;
}
}
if (root->data > key)
{
TreeNode *_n = delete (root->lchild, key);
if (_n)
root->lchild = _n;
}
else
{
TreeNode *_n = delete (root->rchild, key);
if (_n)
root->rchild = _n;
}
return root;
}
// 中序遍历
void zhongxu(TreeNode *root)
{
/**
* 递归输出左子树 => 输出跟结点 => 递归输出右子树
*/
if (NULL == root)
{
return;
}
zhongxu(root->lchild);
printf("%d\t", root->data);
zhongxu(root->rchild);
}
int main()
{
int v[] = {1, 2, 3, 5, 6, 7, 8, 9, 22, 11, 23, 32, 0, 4};
TreeNode *root = NULL;
// printf("%d\r\n", sizeof(v));
for (size_t i = 0; i < sizeof(v) / sizeof(int); i++)
{
root = insert(root, v[i]);
}
zhongxu(root);
root = insert(root, 11);
printf("\r\n");
zhongxu(root);
root = delete (root, 9);
printf("\r\n");
zhongxu(root);
root = delete (root, 20);
printf("\r\n");
zhongxu(root);
}
1.6 平衡二叉树(AVL树:得名于它的发明者G. M. Adelson-Velsky和E. M. Landis)
又名:平衡二叉查找树
AVL树任何结点的两个子树的高度最大差为1,所以也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
AVL树本质还是一棵二叉树,特点是:
- 是一棵二叉搜索树
- 带有平衡条件:每个结点的左右子树高度差绝对值(平衡因子)最多为1
AVL树本质上是带了平衡功能的二叉搜索树。
typedef struct sTreeNode TreeNode;
typedef struct sTreeNode
{
TreeNode *lchild;
TreeNode *rchild;
TreeNode *parent;
int data;
int balance; // 平衡因子 , 0 左右子树高度相等,1 右子树比左子树高1,-1 右子树比左子树矮1
} TreeNode, *PTreeNode;
平衡二叉树的关键就是如何平衡,在插入和删除结点时,可能会破坏树的平衡状态。
- 若结点的平衡因子小于-1,则说明左子树比右子树高;需将结点的左子树的平衡因子调整为-1(若是-1则不需要调整;若不是-1,需左旋结点的左子树),然后右旋结点。
- 若结点的平衡因子大于1,则说明右子树比左子树高;需将结点的右子树的平衡因子调整为-1(若是-1则不需要调整;若不是-1,需右旋结点的右子树),然后左旋结点。
/**
* AVL树:平衡二叉查找树
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
typedef struct sTreeNode TreeNode;
typedef struct sTreeNode
{
TreeNode *lchild;
TreeNode *rchild;
TreeNode *parent;
int data;
int balance; // 平衡因子 , 0 左右子树高度相等,1 右子树比左子树高1,-1 右子树比左子树矮1
} TreeNode, *PTreeNode;
struct TreeNode *new ()
{
int _size = sizeof(TreeNode);
TreeNode *_t = (TreeNode *)malloc(sizeof(TreeNode));
// if (NULL == _t) // out-of-memory
// {
// return NULL;
// }
_t->balance = 0;
_t->lchild = NULL;
_t->rchild = NULL;
_t->parent = NULL;
_t->data = 0;
return _t;
}
void dispose(TreeNode *_node)
{
free(_node);
}
// 查找
struct TreeNode *search(TreeNode *root, int key)
{
if (!root)
return NULL;
if (key == root->data)
return root;
if (root->data > key)
return search(root->lchild, key);
return search(root->rchild, key);
}
// 获取树高度
int height(TreeNode *root)
{
if (!root)
return 0;
int lh = height(root->lchild);
int rh = height(root->rchild);
return lh > rh ? lh + 1 : rh + 1;
}
// 重置节点的balace值
void reset_balance_value(TreeNode *root)
{
if (root)
root->balance = height(root->rchild) - height(root->lchild);
// printf("balance = %d\r\n", root->balance);
}
/**
* 结点 左旋
*/
TreeNode *turn_left(TreeNode *root)
{
// 左旋,将父节点调整为
if (!root)
return NULL;
TreeNode *_root = root->rchild;
TreeNode *_t = _root->lchild;
_root->parent = root->parent;
_root->lchild = root;
_root->lchild->rchild = _t;
return _root;
}
/**
* 结点 右旋
*/
TreeNode *turn_right(TreeNode *root)
{
// 右旋,将父节点调整为右子节点(右子树的根节点,父节点和父节点的右子树组成当前节点右子树);原右子树调整为父节点的左子树
if (!root)
return NULL;
TreeNode *_root = root->lchild;
TreeNode *_t = _root->rchild;
_root->parent = root->parent;
_root->rchild = root;
_root->rchild->lchild = _t;
return _root;
}
/**
* 重新平衡,调整平衡因子绝对值<=1
*/
struct TreeNode *rebalance(TreeNode *root)
{
if (!root)
return NULL;
reset_balance_value(root);
if (root->balance >= -1 && root->balance <= 1)
return root;
if (root->balance < -1) // 左子树更高,通过旋转把左子树balace调整为-1,然后右旋结点;旋转左子树,降低左子树的高度;使左子树的左子树高度大于右子树(左旋左子树),然后右旋
{
if (root->lchild->balance > 0) // 左子树的平衡因子<=0 左子树的左子树不比右子树低,则右旋
{
// 左子树的 右子树更高,左旋,然后右旋
root->lchild = turn_left(root->lchild); // 左旋,高度差绝对值为1时,旋转后的高度不变,高度差由-1变1,1变-1
}
root = turn_right(root);
}
else // 右子树更高,通过旋转把右子树balance调整为1,然后左旋结点
{
if (root->rchild->balance < 0)
{
root->rchild = turn_right(root->rchild);
}
root = turn_left(root);
}
return rebalance(root);
}
/**
* 插入,在bst的插入上加入rebalance操作
*
*/
struct TreeNode *insert(TreeNode *root, int key)
{
if (!root)
{
TreeNode *_n = new ();
_n->data = key;
return _n;
}
if (key == root->data)
{
TreeNode *_n = new ();
_n->data = key;
_n->lchild = root->lchild;
_n->parent = root;
root->lchild = _n;
root = rebalance(root);
return root;
}
if (root->data > key)
{
TreeNode *_n = insert(root->lchild, key);
root->lchild = _n;
root = rebalance(root);
return root;
}
else
{
TreeNode *_n = insert(root->rchild, key);
root->rchild = _n;
root = rebalance(root);
return root;
}
}
struct TreeNode *get_min_node(TreeNode *root)
{
if (root && root->lchild)
return get_min_node(root->lchild);
return root;
}
// 删除
struct TreeNode *delete (TreeNode *root, int key)
{
if (!root)
return NULL;
if (key == root->data)
{
if (!root->rchild) // 右结点为空
{
// 返回左节点
TreeNode *_n = root->lchild;
if (_n)
{
_n->parent = NULL;
}
dispose(root); // 释放root节点
_n = rebalance(_n);
return _n;
}
else
{
TreeNode *_n = get_min_node(root->rchild);
_n->parent->lchild = NULL;
_n->parent = NULL;
_n->lchild = root->lchild;
_n->rchild = root->rchild;
dispose(root); // 释放root节点
_n = rebalance(_n);
return _n;
}
}
if (root->data > key)
{
TreeNode *_n = delete (root->lchild, key);
if (_n)
{
root->lchild = _n;
_n = rebalance(_n);
}
}
else
{
TreeNode *_n = delete (root->rchild, key);
if (_n)
{
root->rchild = _n;
_n = rebalance(_n);
}
}
return root;
}
// 中序遍历
void zhongxu(TreeNode *root)
{
/**
* 递归输出左子树 => 输出跟结点 => 递归输出右子树
*/
if (NULL == root)
{
return;
}
zhongxu(root->lchild);
printf("%d\t", root->data);
zhongxu(root->rchild);
}
int min_size = 8;
int start = 8;
// 将数据填入数组的指定位置,无数据节点填-1;类似层次遍历
void fill(TreeNode *root, const int index, int *vl)
{
if (!root)
{
vl[index] = -1;
return;
}
vl[index] = root->data;
// printf("vl[%d] = %d\r\n", index, vl[index]);
fill(root->lchild, (2 * index) + 1, vl);
fill(root->rchild, (2 * index) + 2, vl);
}
/**
* 输出树形结构
*/
void show(TreeNode *root)
{
if (!root)
{ printf("empty tree"); return;}
int _depth = height(root);
//TreeNode *_n = root;
int *_vl = (int *)malloc(sizeof(int) * ((1 << (_depth + 1)) - 1));
// 初始化
memset(_vl, -1, sizeof(int) * ((1 << (_depth + 1)) - 1));
// 将数据填入数组,无数据节点填-1;类似层次遍历
fill(root, 0, _vl);
for (size_t i = 0; i < _depth; i++)
{
int w = 1 << (_depth - i + 1);
for (size_t j = ((1 << i) - 1); j < ((1 << (i + 1)) - 1); j++)
{
if (_vl[j] < 0)
printf("%*c%*c", w, '-', w, ' ');
else
printf("%*d%*c", w, _vl[j], w, ' ');
}
printf("\r\n");
}
free(_vl);
}
int main()
{
int v[] = {1, 2, 3, 5, 6, 7, 8, 9, 22, 11, 23, 32, 0, 4};
TreeNode *root = NULL;
for (size_t i = 0; i < sizeof(v) / sizeof(int); i++)
{
root = insert(root, v[i]);
show(root);
printf("\r\n\r\n################################################################################################################\r\n\r\n");
}
// zhongxu(root);
// printf("\r\n");
// show(root);
root = insert(root, 11);
show(root);
printf("\r\n\r\n################################################################################################################\r\n\r\n");
root = delete (root, 9);
show(root);
}
1.7 红黑树(RED-BLACK TREE)
红黑树是一棵二叉查找树(AVL树:自平衡二叉查找树),它在每个结点上增加了一个存储位来表示结点的颜色,可以是红色或黑色。通过对任何一条从根到叶子的简单路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍,因而是近似平衡的。
typedef struct sRBTree
{
RBTree * lchild;
RBTree * rchild;
BYTE color; // 颜色 红或黑
KEY key;
void * data;
}RBTree;
红黑树性质:
- 每个结点或是红色或黑色
- 根节点是黑色
- 叶子结点是黑色
- 如果一个结点是红色,则它的两个子结点都是黑色
- 对于每个结点,从该结点到其所有后代叶子结点的简单路径上,均包含相同数目的黑色结点
/**
* 红黑树
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#define COLOR_BLACK 0
#define COLOR_RED 1
typedef struct sTreeNode TreeNode;
typedef struct sTreeNode
{
TreeNode *lchild;
TreeNode *rchild;
TreeNode *parent;
int data;
int color; // 颜色 0 黑色,1 红色
} TreeNode, *PTreeNode;
struct TreeNode *new ()
{
int _size = sizeof(TreeNode);
TreeNode *_t = (TreeNode *)malloc(sizeof(TreeNode));
// if (NULL == _t) // out-of-memory
// {
// return NULL;
// }
_t->color = COLOR_RED; // 新结点都是红色的
_t->lchild = NULL;
_t->rchild = NULL;
_t->parent = NULL;
_t->data = 0;
return _t;
}
void dispose(TreeNode *_node)
{
free(_node);
}
// 查找,二叉排序树的查找
struct TreeNode *search(TreeNode *root, int key)
{
if (!root)
return NULL;
if (key == root->data)
return root;
if (root->data > key)
return search(root->lchild, key);
return search(root->rchild, key);
}
/**
* 结点 左旋
*/
void turn_left(TreeNode *root)
{
// 左旋,将父节点调整为
/**
* 左旋需要调整4个结点共6个元素
*
* 需调整4个结点:
* 1. root
* 2. root 结点的父节点 P
* 3. root 节点的右子节点 RC
* 4. RC 结点的左子节点 RC_LC
*
* 需调整6个元素:
* 1. root 的父结点指针(指向RC)
* 2. root 的右子结点指针(指向RC_LC)
*
* 3. RC 的父结点指针(指向P)
* 4. RC 的左子结点指针(指向root)
*
* 5. RC_LC 的父结点指针(指向root)
* 6. P的左子结点指针或右子结点指针(若root为P的左子结点,则改P的左子结点指向RC,否则右子结点指向RC)
*
*
*
*
*
* P P
* \ \
* root ===> RC
* / \ / \
* * RC root *
* / \ / \
* RC_LC * * RC_LC
*/
// 结点为NULL,或右子结点为NULL,不能左旋
if (!root || !root->rchild)
return;
// root是否为其父结点的左子结点
char root_is_left = 0;
if (root->parent && root == root->parent->lchild)
root_is_left = 1;
// root的父结点
TreeNode *P = root->parent;
// root的右子结点
TreeNode *RC = root->rchild;
// RC的左子结点
TreeNode *RC_LC = RC->lchild;
root->parent = RC;
root->rchild = RC_LC;
RC->parent = P;
RC->lchild = root;
if (RC_LC)
RC_LC->parent = root;
// 调整父结点的指针
if (P)
if (root_is_left)
P->lchild = RC;
else
P->rchild = RC;
}
/**
* 结点 右旋
*/
void turn_right(TreeNode *root)
{
// 右旋,将父节点调整为右子节点(右子树的根节点,父节点和父节点的右子树组成当前节点右子树);原右子树调整为父节点的左子树
/**
* 右旋需要调整4个结点共6个元素
*
* 需调整4个结点:
* 1. root
* 2. root 结点的父节点 P
* 3. root 节点的左子节点 LC
* 4. LC 结点的右子节点 LC_RC
*
* 需调整6个元素:
* 1. root 的父结点指针(指向LC)
* 2. root 的左子结点指针(指向LC_RC)
*
* 3. LC 的父结点指针(指向P)
* 4. LC 的右子结点指针(指向root)
*
* 5. LC_RC 的父结点指针(指向root)
* 6. P的左子结点指针或右子结点指针(若root为P的左子结点,则改P的左子结点指向LC,否则右子结点指向LC)
*
*
*
*
*
* P P
* \ \
* root ===> LC
* / \ / \
* LC * * root
* / \ / \
* * LC_RC LC_RC *
*/
// 结点为NULL,或左子节点为NULL 不能右旋
if (!root || !root->lchild)
return NULL;
// root是否为其父结点的左子结点
char root_is_left = 0;
if (root->parent && root == root->parent->lchild)
root_is_left = 1;
// root的父结点
TreeNode *P = root->parent;
// root的左子结点
TreeNode *LC = root->lchild;
// LC的右子结点
TreeNode *LC_RC = LC->rchild;
root->parent = LC;
root->lchild = LC_RC;
LC->parent = P;
LC->rchild = root;
if (LC_RC)
LC_RC->parent = root;
// 调整父结点的指针
if (P)
if (root_is_left)
P->lchild = LC;
else
P->rchild = LC;
}
/**
* 重新平衡,调整插入节点root后的平衡;变色和旋转
*/
void rebalance(TreeNode *root)
{
if (!root->parent) // root为根节点
{
// case 1
root->color = COLOR_BLACK;
return;
}
// 父结点
TreeNode *_p = root->parent;
if (COLOR_BLACK == _p->color) // 插入结点的父结点是黑色,不需要调整平衡
{
// case 2
root->color = COLOR_RED;
return;
}
/* 父结点为红色,则一定存在祖父结点 */
// 祖父结点
TreeNode *_pp = _p->parent;
// 叔叔结点
TreeNode *_u = NULL;
// 父结点是祖父结点的左子结点
char _p_is_left = 0;
if (_p == _pp->lchild)
{
_u = _pp->rchild;
_p_is_left = 1;
}
else
_u = _pp->lchild;
if (_u && COLOR_RED == _u->color)
{
// case 3.1
_p->color = _u->color = COLOR_BLACK;
_pp->color = COLOR_RED;
rebalance(_pp);
}
else
{
// case 3.2
// 当前结点是父结点的左子结点
char c_is_left = 0;
if (root == _p->lchild)
c_is_left = 1;
if (_p_is_left)
{
// case 3.2.1
if (!c_is_left)
{
// case 3.2.1.2
turn_left(_p);
// 将父结点调整为插入结点
TreeNode *_tmp = root;
root = _p;
_p = _tmp;
}
// case 3.2.1.1
_p->color = COLOR_BLACK;
_pp->color = COLOR_RED;
turn_right(_pp);
}
else
{
// case 3.2.2
if (c_is_left)
{
// case 3.2.2.2
turn_right(_p);
// 将父结点调整为插入结点
TreeNode *_tmp = root;
root = _p;
_p = _tmp;
}
// case 3.2.2.1
_p->color = COLOR_BLACK;
_pp->color = COLOR_RED;
turn_left(_pp);
}
}
}
/**
* 插入
*
*/
TreeNode *insert(TreeNode *root, int key)
{
if (!root)
{
TreeNode *_n = new ();
_n->data = key;
_n->color = COLOR_BLACK;
return _n;
}
else
{
TreeNode *_new = NULL;
if (key == root->data)
{
TreeNode *_n = new ();
_n->data = key;
_n->lchild = root->lchild;
_n->parent = root;
root->lchild = _n;
_new = _n;
}
else if (root->data > key)
{
if (root->lchild)
{
insert(root->lchild, key);
}
else
{
TreeNode *_n = new ();
_n->data = key;
_n->parent = root;
root->lchild = _n;
_new = _n;
}
}
else
{
if (root->rchild)
{
insert(root->rchild, key);
}
else
{
TreeNode *_n = new ();
_n->data = key;
_n->parent = root;
root->rchild = _n;
_new = _n;
}
}
// 插入新结点需重新平衡
if (_new)
rebalance(_new);
}
return root;
}
// 将数据填入数组的指定位置,无数据节点填-1;类似层次遍历
void fill(TreeNode *root, const int index, TreeNode **vl)
{
if (!root)
{
vl[index] = NULL;
return;
}
vl[index] = root;
fill(root->lchild, (2 * index) + 1, vl);
fill(root->rchild, (2 * index) + 2, vl);
}
// 获取树高度
int height(TreeNode *root)
{
if (!root)
return 0;
int lh = height(root->lchild);
int rh = height(root->rchild);
return lh > rh ? lh + 1 : rh + 1;
}
/**
* 输出树形结构
*/
void show(TreeNode *root)
{
if (!root)
printf("empty tree");
int _depth = height(root);
TreeNode *_n = root;
TreeNode **_vl = (TreeNode *)malloc(sizeof(TreeNode *) * ((1 << (_depth + 1)) - 1));
memset(_vl, 0, sizeof(TreeNode *) * ((1 << (_depth + 1)) - 1));
// 将数据填入数组,无数据节点填NULL;类似层次遍历
fill(_n, 0, _vl);
for (size_t i = 0; i < _depth; i++)
{
int w = 1 << (_depth - i + 1);
for (size_t j = ((1 << i) - 1); j < ((1 << (i + 1)) - 1); j++)
{
TreeNode *_tmp = _vl[j];
if (NULL == _vl[j])
printf("%*c%*c", w, '-', w, ' ');
else if (COLOR_BLACK == _vl[j]->color)
printf("\033[40;37m %*d%*c \033[0m", w - 1, _vl[j]->data, w - 1, ' ');
else
printf("\033[40;31m %*d%*c \033[0m", w - 1, _vl[j]->data, w - 1, ' ');
}
printf("\r\n");
}
free(_vl);
}
TreeNode *get_root(TreeNode *root)
{
if (root->parent)
return get_root(root->parent);
return root;
}
// 用于调试,判断树形结构是否正确
void check(TreeNode *root)
{
if (root)
{
if (root->lchild)
{
if (root != root->lchild->parent)
{
printf("error\r\n");
}
check(root->lchild);
}
if (root->rchild)
{
if (root != root->rchild->parent)
{
printf("error\r\n");
}
check(root->rchild);
}
}
}
int main()
{
TreeNode *root = NULL;
int vs[] = {1, 11, 5, 2, 3, 23, 32, 6, 7, 8, 9, 22, 100, 4};
for (size_t i = 0; i < sizeof(vs) / sizeof(int); i++)
{
root = insert(root, vs[i]);
root = get_root(root);
// check(root);
// printf("\r\n##################################################################################################################################\r\n");
show(root);
}
// int v;
// while (scanf("%d", &v))
// {
// root = insert(root, v);
// printf("insert ok\r\n");
// show(root);
// }
}
**红黑树通常用于实现map或set,所以不允许有重复值。**若是键值型数据,在插入时找到重复键则直接替换此结点的值。
1.7.1 红黑树删除平衡
/**
* 红黑树
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#define COLOR_BLACK 0
#define COLOR_RED 1
typedef struct sTreeNode TreeNode;
typedef struct sTreeNode
{
TreeNode *lchild;
TreeNode *rchild;
TreeNode *parent;
int data;
int color; // 颜色 0 黑色,1 红色
} TreeNode, *PTreeNode;
struct TreeNode *new ()
{
int _size = sizeof(TreeNode);
TreeNode *_t = (TreeNode *)malloc(sizeof(TreeNode));
// if (NULL == _t) // out-of-memory
// {
// return NULL;
// }
_t->color = COLOR_RED; // 新结点都是红色的
_t->lchild = NULL;
_t->rchild = NULL;
_t->parent = NULL;
_t->data = 0;
return _t;
}
void dispose(TreeNode *_node)
{
free(_node);
}
// 查找,二叉排序树的查找
struct TreeNode *search(TreeNode *root, int key)
{
if (!root)
return NULL;
if (key == root->data)
return root;
if (root->data > key)
return search(root->lchild, key);
return search(root->rchild, key);
}
/**
* 结点 左旋
*/
void turn_left(TreeNode *root)
{
// 左旋,将父节点调整为
/**
* 左旋需要调整4个结点共6个元素
*
* 需调整4个结点:
* 1. root
* 2. root 结点的父节点 P
* 3. root 节点的右子节点 RC
* 4. RC 结点的左子节点 RC_LC
*
* 需调整6个元素:
* 1. root 的父结点指针(指向RC)
* 2. root 的右子结点指针(指向RC_LC)
*
* 3. RC 的父结点指针(指向P)
* 4. RC 的左子结点指针(指向root)
*
* 5. RC_LC 的父结点指针(指向root)
* 6. P的左子结点指针或右子结点指针(若root为P的左子结点,则改P的左子结点指向RC,否则右子结点指向RC)
*
*
*
*
*
* P P
* \ \
* root ===> RC
* / \ / \
* * RC root *
* / \ / \
* RC_LC * * RC_LC
*/
// 结点为NULL,或右子结点为NULL,不能左旋
if (!root || !root->rchild)
return;
// root是否为其父结点的左子结点
char root_is_left = 0;
if (root->parent && root == root->parent->lchild)
root_is_left = 1;
// root的父结点
TreeNode *P = root->parent;
// root的右子结点
TreeNode *RC = root->rchild;
// RC的左子结点
TreeNode *RC_LC = RC->lchild;
root->parent = RC;
root->rchild = RC_LC;
RC->parent = P;
RC->lchild = root;
if (RC_LC)
RC_LC->parent = root;
// 调整父结点的指针
if (P)
if (root_is_left)
P->lchild = RC;
else
P->rchild = RC;
}
/**
* 结点 右旋
*/
void turn_right(TreeNode *root)
{
// 右旋,将父节点调整为右子节点(右子树的根节点,父节点和父节点的右子树组成当前节点右子树);原右子树调整为父节点的左子树
/**
* 右旋需要调整4个结点共6个元素
*
* 需调整4个结点:
* 1. root
* 2. root 结点的父节点 P
* 3. root 节点的左子节点 LC
* 4. LC 结点的右子节点 LC_RC
*
* 需调整6个元素:
* 1. root 的父结点指针(指向LC)
* 2. root 的左子结点指针(指向LC_RC)
*
* 3. LC 的父结点指针(指向P)
* 4. LC 的右子结点指针(指向root)
*
* 5. LC_RC 的父结点指针(指向root)
* 6. P的左子结点指针或右子结点指针(若root为P的左子结点,则改P的左子结点指向LC,否则右子结点指向LC)
*
*
*
*
*
* P P
* \ \
* root ===> LC
* / \ / \
* LC * * root
* / \ / \
* * LC_RC LC_RC *
*/
// 结点为NULL,或左子节点为NULL 不能右旋
if (!root || !root->lchild)
return NULL;
// root是否为其父结点的左子结点
char root_is_left = 0;
if (root->parent && root == root->parent->lchild)
root_is_left = 1;
// root的父结点
TreeNode *P = root->parent;
// root的左子结点
TreeNode *LC = root->lchild;
// LC的右子结点
TreeNode *LC_RC = LC->rchild;
root->parent = LC;
root->lchild = LC_RC;
LC->parent = P;
LC->rchild = root;
if (LC_RC)
LC_RC->parent = root;
// 调整父结点的指针
if (P)
if (root_is_left)
P->lchild = LC;
else
P->rchild = LC;
}
/**
* 重新平衡,调整插入节点root后的平衡;变色和旋转
*/
void rebalance(TreeNode *root)
{
if (!root->parent) // root为根节点
{
// case 1
root->color = COLOR_BLACK;
return;
}
// 父结点
TreeNode *_p = root->parent;
if (COLOR_BLACK == _p->color) // 插入结点的父结点是黑色,不需要调整平衡
{
// case 2
root->color = COLOR_RED;
return;
}
/* 父结点为红色,则一定存在祖父结点 */
// 祖父结点
TreeNode *_pp = _p->parent;
// 叔叔结点
TreeNode *_u = NULL;
// 父结点是祖父结点的左子结点
char _p_is_left = 0;
if (_p == _pp->lchild)
{
_u = _pp->rchild;
_p_is_left = 1;
}
else
_u = _pp->lchild;
if (_u && COLOR_RED == _u->color)
{
// case 3.1
_p->color = _u->color = COLOR_BLACK;
_pp->color = COLOR_RED;
rebalance(_pp);
}
else
{
// case 3.2
// 当前结点是父结点的左子结点
char c_is_left = 0;
if (root == _p->lchild)
c_is_left = 1;
if (_p_is_left)
{
// case 3.2.1
if (!c_is_left)
{
// case 3.2.1.2
turn_left(_p);
// 将父结点调整为插入结点
TreeNode *_tmp = root;
root = _p;
_p = _tmp;
}
// case 3.2.1.1
_p->color = COLOR_BLACK;
_pp->color = COLOR_RED;
turn_right(_pp);
}
else
{
// case 3.2.2
if (c_is_left)
{
// case 3.2.2.2
turn_right(_p);
// 将父结点调整为插入结点
TreeNode *_tmp = root;
root = _p;
_p = _tmp;
}
// case 3.2.2.1
_p->color = COLOR_BLACK;
_pp->color = COLOR_RED;
turn_left(_pp);
}
}
}
/**
* 插入
*
*/
TreeNode *insert(TreeNode *root, int key)
{
if (!root)
{
TreeNode *_n = new ();
_n->data = key;
_n->color = COLOR_BLACK;
return _n;
}
else
{
TreeNode *_new = NULL;
if (key == root->data)
{
TreeNode *_n = new ();
_n->data = key;
_n->lchild = root->lchild;
_n->parent = root;
root->lchild = _n;
_new = _n;
}
else if (root->data > key)
{
if (root->lchild)
{
insert(root->lchild, key);
}
else
{
TreeNode *_n = new ();
_n->data = key;
_n->parent = root;
root->lchild = _n;
_new = _n;
}
}
else
{
if (root->rchild)
{
insert(root->rchild, key);
}
else
{
TreeNode *_n = new ();
_n->data = key;
_n->parent = root;
root->rchild = _n;
_new = _n;
}
}
// 插入新结点需重新平衡
if (_new)
rebalance(_new);
}
return root;
}
void reblance_del(TreeNode *node)
{
if (!node)
return;
TreeNode *Tnil = new ();
Tnil->color = COLOR_BLACK;
TreeNode *B = NULL; //兄弟结点
char node_is_left = 0;
TreeNode *BL = NULL;
TreeNode *BR = NULL;
while (node->parent && COLOR_BLACK == node->color)
{
if (node == node->parent->lchild)
{
node_is_left = 1;
B = node->parent->rchild;
}
else
{
node_is_left = 0;
B = node->parent->lchild;
}
BL = B->lchild ? B->lchild : Tnil;
BR = B->rchild ? B->rchild : Tnil;
if (node_is_left)
{
if (COLOR_RED == B->color)
{
B->color = COLOR_BLACK;
B->parent->color = COLOR_RED; // 父结点和兄弟结点互换颜色
turn_left(B->parent); // 兄弟结点变黑色
B = node->parent->rchild; // 兄弟结点变了
BL = B->lchild ? B->lchild : Tnil;
BR = B->rchild ? B->rchild : Tnil;
}
if (COLOR_BLACK == BL->color && COLOR_BLACK == BR->color)
{
B->color = COLOR_RED; // 两兄弟黑高都-1,以父结点为新的平衡点
node = node->parent;
}
else // 有子节点不为黑色
{
if (COLOR_BLACK == BR->color) // 左子结点是红色
{
B->color = COLOR_RED;
BL->color = COLOR_BLACK; // B和BL交换颜色
turn_right(B);
B = node->parent->rchild; // 兄弟结点的右子结点是红色了
BL = B->lchild ? B->lchild : Tnil;
BR = B->rchild ? B->rchild : Tnil;
}
// 兄弟的右子结点是红色
B->color = B->parent->color;
B->parent->color = COLOR_BLACK; // 兄弟结点和父结点交换颜色
BR->color = COLOR_BLACK;
turn_left(B->parent); // 左旋父结点,完成平衡
node = Tnil;
}
}
else
{
if (COLOR_RED == B->color)
{
B->color = COLOR_BLACK;
B->parent->color = COLOR_RED; // 父结点和兄弟结点互换颜色
turn_right(B->parent); // 兄弟结点变黑色
B = node->parent->lchild; // 兄弟结点变了
BL = B->lchild ? B->lchild : Tnil;
BR = B->rchild ? B->rchild : Tnil;
}
if (COLOR_BLACK == BL->color && COLOR_BLACK == BR->color)
{
B->color = COLOR_RED; // 两兄弟黑高都-1,以父结点为新的平衡点
node = node->parent;
}
else // 有子节点不为黑色
{
if (COLOR_BLACK == BL->color) // 右子结点是红色
{
B->color = COLOR_RED;
BR->color = COLOR_BLACK; // B和BR交换颜色
turn_left(B);
B = node->parent->lchild; // 兄弟结点的左子结点是红色了
BL = B->lchild ? B->lchild : Tnil;
BR = B->rchild ? B->rchild : Tnil;
}
// 兄弟的左子结点是红色
B->color = B->parent->color;
B->parent->color = COLOR_BLACK; // 兄弟结点和父结点交换颜色
BL->color = COLOR_BLACK;
turn_right(B->parent); // 右旋父结点,完成平衡
node = Tnil;
}
}
}
node->color = COLOR_BLACK;
}
TreeNode *get_replace_node(TreeNode *node)
{
// 2. 查找替换结点
/**
* 2.1 无子结点,直接删除,替换结点是其本身
* 2.2 无右子结点,替换结点是其左子结点,用替换结点再查找替换结点
* 2.3 有右子结点,替换结点是其后继结点,用替换结点再查找替换结点
*/
if (!node)
return node;
if (node->lchild && node->rchild)
{
#if 1
// 有右子结点
TreeNode *_next_node = node->rchild;
while (_next_node->lchild)
{
_next_node = _next_node->lchild;
}
return _next_node;
#else
TreeNode *_next_node = node->lchild;
while (_next_node->rchild)
{
_next_node = _next_node->rchild;
}
return _next_node;
#endif
}
return node;
#if 0
if (!node->lchild)
{
return get_replace_node(node->rchild);
}
TreeNode *_next_node = node->lchild;
while (_next_node->rchild)
{
_next_node = _next_node->rchild;
}
return _next_node;
#else
// if (!node->lchild) // 左子节点为空
// return node->rchild;
// // 没有右子结点
// else if (!node->rchild)
// {
// return node->lchild;
// }
// // 有右子结点
// TreeNode *_next_node = node->rchild;
// while (_next_node->lchild)
// {
// _next_node = _next_node->lchild;
// }
// return _next_node;
#endif
}
// 将数据填入数组的指定位置,无数据节点填-1;类似层次遍历
void fill(TreeNode *root, const int index, TreeNode **vl)
{
if (!root)
{
vl[index] = NULL;
return;
}
vl[index] = root;
fill(root->lchild, (2 * index) + 1, vl);
fill(root->rchild, (2 * index) + 2, vl);
}
// 获取树高度
int height(TreeNode *root)
{
if (!root)
return 0;
int lh = height(root->lchild);
int rh = height(root->rchild);
return lh > rh ? lh + 1 : rh + 1;
}
/**
* 输出树形结构
*/
void show(TreeNode *root)
{
if (!root)
printf("empty tree");
int _depth = height(root);
TreeNode *_n = root;
TreeNode **_vl = (TreeNode *)malloc(sizeof(TreeNode *) * ((1 << (_depth + 1)) - 1));
memset(_vl, 0, sizeof(TreeNode *) * ((1 << (_depth + 1)) - 1));
// 将数据填入数组,无数据节点填NULL;类似层次遍历
fill(_n, 0, _vl);
for (size_t i = 0; i < _depth; i++)
{
int w = 1 << (_depth - i + 1);
for (size_t j = ((1 << i) - 1); j < ((1 << (i + 1)) - 1); j++)
{
TreeNode *_tmp = _vl[j];
if (NULL == _vl[j])
printf("%*c%*c", w, '-', w, ' ');
else if (COLOR_BLACK == _vl[j]->color)
printf("\033[40;37m %*d%*c \033[0m", w - 1, _vl[j]->data, w - 1, ' ');
else
printf("\033[40;31m %*d%*c \033[0m", w - 1, _vl[j]->data, w - 1, ' ');
}
printf("\r\n");
}
free(_vl);
}
TreeNode *get_root(TreeNode *root)
{
if (root->parent)
return get_root(root->parent);
return root;
}
// 用于调试,判断树形结构是否正确
void check(TreeNode *root)
{
if (root)
{
if (root->lchild)
{
if (root != root->lchild->parent)
{
printf("error\r\n");
}
check(root->lchild);
}
if (root->rchild)
{
if (root != root->rchild->parent)
{
printf("error\r\n");
}
check(root->rchild);
}
}
}
void delete (TreeNode *root, int key)
{
/**
* 1. 找到结点
* 2. 找到替换结点及替换结点的接替结点
* 3. 用替换结点数据替换删除结点数据,接替结点移到替换结点位置
* 4. 平衡替换结点(如果替换节点是黑色,需平衡其接替结点)
*/
// 1. 查找删除结点
TreeNode *_node = search(root, key);
if (!_node)
return; // 未找到结点
// 2. 查找替换结点
TreeNode *_rb_node = get_replace_node(_node); // 替换结点最多只有一个子结点(无左子结点,可能有右子结点)
// 3. 修改数据
_node->data = _rb_node->data; // 用替换结点数据替换删除结点数据
TreeNode *__root = get_root(_node);
printf("\r\n##################################################################################################################################\r\n");
show(__root);
if (COLOR_BLACK == _rb_node->color)
// 4. 平衡替换结点
reblance_del(_rb_node);
__root = get_root(_node);
printf("\r\n##################################################################################################################################\r\n");
show(__root);
#if 0
// 5. 删除替换结点(替换结点一定没有左子结点,可能有右子结点)
TreeNode *P = _rb_node->parent;
TreeNode *RC = _rb_node->rchild;
if (_rb_node == P->lchild)
{
P->lchild = NULL;
if (RC)
{
RC->parent = P;
P->lchild = RC;
}
free(_rb_node);
}
else
{
P->rchild = NULL;
if (RC)
{
RC->parent = P;
P->rchild = RC;
}
free(_rb_node);
}
#else
// 5. 删除替换结点
TreeNode *P = _rb_node->parent;
TreeNode *LC = _rb_node->lchild;
TreeNode *RC = _rb_node->rchild;
if (!P)
{
// 删除的是根结点,最多只有一个子结点
if (!RC && !LC) // 没有子结点
{
// 删掉就没了
root->data = -1;
}
else if (!LC) // 没有左子结点
{
/* code */
// root->color = COLOR_BLACK;
root->data = RC->data;
root->lchild = NULL;
root->rchild = NULL;
free(RC);
}
else
{
// root->color = COLOR_BLACK;
root->data = LC->data;
root->lchild = NULL;
root->rchild = NULL;
free(LC);
}
}
else if (_rb_node == P->lchild)
{
P->lchild = NULL;
if (LC)
{
LC->parent = P;
P->lchild = LC;
}
else if (RC)
{
RC->parent = P;
P->lchild = RC;
}
free(_rb_node);
}
else
{
P->rchild = NULL;
if (LC)
{
LC->parent = P;
P->rchild = LC;
}
else if (RC)
{
RC->parent = P;
P->rchild = RC;
}
free(_rb_node);
}
#endif
// __root = get_root(_node);
// printf("\r\n##################################################################################################################################\r\n");
// show(__root);
}
int main()
{
TreeNode *root = NULL;
int vs[] = {1, 11, 5, 2, 3, 23, 32, 6, 7, 8, 9, 22, 100, 4};
for (size_t i = 0; i < sizeof(vs) / sizeof(int); i++)
{
root = insert(root, vs[i]);
root = get_root(root);
// check(root);
// printf("\r\n##################################################################################################################################\r\n");
// show(root);
}
show(root);
// int v;
// while (scanf("%d", &v))
// {
// root = insert(root, v);
// // printf("insert ok\r\n");
// printf("\r\n##################################################################################################################################\r\n");
// show(root);
// }
int v;
while (scanf("%d", &v))
{
delete (root, v);
// printf("insert ok\r\n");
root = get_root(root);
// printf("insert ok\r\n");
printf("\r\n##################################################################################################################################\r\n");
show(root);
}
// delete (root, 7);
// root = get_root(root);
// // printf("insert ok\r\n");
// printf("\r\n##################################################################################################################################\r\n");
// show(root);
}
1.5 树和森林
1.6 哈夫曼树
最优二叉树,带权路径长度最小的树,最适合用于做无损压缩编码(哈夫曼编码)。没有
哈夫曼树和哈夫曼编码
- 权越大离根结点越近
- 没有度为1的结点(没有只有一颗子树的节点)
struct elemtype
{
/* data */
};
// 二叉链表
struct dtreenode
{
elemtype data;
dtreenode *lchild; //左子节点
dtreenode *rchild; //右子节点
};
// 二叉链表
struct ttreenode
{
elemtype data;
ttreenode *parent; //父节点
ttreenode *lchild; //左子节点
ttreenode *rchild; //右子节点
};