本文所有代码均在仓库中,这是一个完整的由纯C语言实现的可以存储任意类型元素的数据结构的工程项目。
最后也是最重要的一点,数据结构的通用性和舒适的体验感,下面以平衡二叉树为例:
#include "tree-structure/balanced-binary-tree/BalancedBinaryTree.h"
#include "tree-structure/balanced-binary-tree/BalancedBinaryTree.h"
int dataCompare(void *, void *);
typedef struct People {
char *name;
int age;
} *People;
int main(int argc, char **argv) {
struct People dataList[] = {
{"张三", 15},
{"李四", 3},
{"王五", 7},
{"赵六", 10},
{"田七", 9},
{"周八", 8},
};
BalancedBinaryTree tree = balancedBinaryTreeConstructor(NULL, 0, dataCompare);
for (int i = 0; i < 6; ++i) {
balancedBinaryTreeInsert(&tree, dataList + i, dataCompare);
}
return 0;
}
/**
* 根据人的年龄比较
*/
int dataCompare(void *data1, void *data2) {
int sub = ((People) data1)->age - ((People) data2)->age;
if (sub > 0) {
return 1;
} else if (sub < 0) {
return -1;
} else {
return 0;
}
}
#include "tree-structure/balanced-binary-tree/BalancedBinaryTree.h"
int dataCompare(void *, void *);
void dataPrint(void *);
typedef struct People {
char *name;
int age;
} *People;
int main(int argc, char **argv) {
struct People dataList[] = {
{"张三", 15},
{"李四", 3},
{"王五", 7},
{"赵六", 10},
{"田七", 9},
{"周八", 8},
};
BalancedBinaryTree tree = balancedBinaryTreeConstructor(NULL, 0, dataCompare);
for (int i = 0; i < 6; ++i) {
balancedBinaryTreeInsert(&tree, dataList + i, dataCompare);
balancedBinaryTreePrint(tree, dataPrint);
printf("-------------\n");
}
return 0;
}
/**
* 根据人的年龄比较
*/
int dataCompare(void *data1, void *data2) {
int sub = ((People) data1)->age - ((People) data2)->age;
if (sub > 0) {
return 1;
} else if (sub < 0) {
return -1;
} else {
return 0;
}
}
/**
* 打印人的年龄
* @param data
*/
void dataPrint(void *data) {
People people = (People) data;
printf("%d", people->age);
}
打印的结果如下:
最后期待大佬们的点赞。
树是由 n n n个结点组成的有限集,其中每个结点有零个或多个子结点以及零个或一个父结点,没有父结点的结点称为根结点,每一个非根结点有且只有一个父结点。除根结点外的其余结点可以分为 m m m个互不相交的有限集,其中每个有限集本身就是一棵树,这些树又称为根的子树。森林就是 m m m棵不相交的树的集合,树一定是森林,但森林不一定是树。
树一般有以下三种存储方式:
双亲表示法采用一组连续的空间存储每一个结点,同时在每个结点中增设一个伪指针,这个伪指针指向其双亲结点在连续空间中的位置:
struct ParentTreeNode {
void *data;
int parent;
};
struct ParentTree {
struct ParentTreeNode *nodeList;
int size;
int nodeCount;
};
在孩子表示法中,每个结点不再增设一个伪指针,而是增设一个存储当前结点孩子下标的链表:
struct ChildTreeNode {
void *data;
int index; //表示当前接结点在数组中的位置,仅在链表中使用
struct ChildTreeNode *child;
};
struct ChildTree {
struct ChildTreeNode *nodeList;
int size;
int nodeCount;
};
孩子兄弟表示法又称为二叉树表示法,它采用二叉链表作为树的存储结构,其中每个结点包含三部分内容:结点值、指向第一个孩子结点的指针以及指向下一个兄弟结点的指针:
struct ChildSiblingTreeNode {
void *data;
struct ChildSiblingTreeNode *firstChild;
struct ChildSiblingTreeNode *nextSibling;
};
二叉树是指每个结点最多只有两棵子树且子树之间顺序不能颠倒的树,这两棵子树分别称为左子树和右子树。二叉树与度为 2 2 2的有序树的区别如下:
二叉树的性质如下:
二叉树在第 i i i层至多有 2 i − 1 2^{i-1} 2i−1个结点,最少有 1 1 1个结点。
高度为 h h h的二叉树最多有 2 h − 1 2^h-1 2h−1个结点。
设非空二叉树中度为 0 、 1 、 2 0、1、2 0、1、2的结点的个数分别为 n 1 、 n 2 、 n 3 n_1、n_2、n_3 n1、n2、n3,设树中结点的总个数为 n n n,则
对于任意一颗二叉树,如果对其进行编号,那么结点编号所对应的二进制可以反应从根节点到该结点的路径,其中最高位是 1 1 1,其余各位从高到低表示从根节点到第 k k k个节点的移动方向, 0 0 0表示移动到左子节点, 1 1 1表示移动到右子节点。
二叉树的顺序存储是指用一组地址连续的存储单元依次自上而下、自左向右存储完全二叉树上的结点元素,这样树中结点的序号就可以唯一反应结点之间的关系。对于不是完全二叉树的二叉树,需要填充空结点让其每个结点与完全二叉树上的结点对应。因此非完全二叉树的二叉树通常采用链式存储,链表结点至少包含三个域:数据域、左孩子指针域和右孩子指针域。还可以增加一个指针域指向其双亲。
struct BinaryTreeNode {
void *data;
struct BinaryTreeNode *lNode;
struct BinaryTreeNode *rNode;
};
遍历一棵二叉树要决定对根节点N、、左子树L和右子树R的访问顺序,常见的访问顺序如下:
void preOrder(BinaryTree tree, LinkedQueue preOrderList) {
if (tree != NULL) {
linkedQueueEnQueue(preOrderList, tree->data);
preOrder(tree->lNode, preOrderList);
preOrder(tree->rNode, preOrderList);
}
}
void inOrder(BinaryTree tree, LinkedQueue inOrderList) {
if (tree != NULL) {
inOrder(tree->lNode, inOrderList);
linkedQueueEnQueue(inOrderList, tree->data);
inOrder(tree->rNode, inOrderList);
}
}
void postOrder(BinaryTree tree, LinkedQueue postOrderList) {
if (tree != NULL) {
postOrder(tree->lNode, postOrderList);
postOrder(tree->rNode, postOrderList);
linkedQueueEnQueue(postOrderList, tree->data);
}
}
void levelOrder(BinaryTree tree, LinkedQueue levelOrderList) {
LinkedQueue queue = linkedQueueConstructor();
linkedQueueEnQueue(queue, tree);
while (!linkedQueueIsEmpty(queue)) {
BinaryTreeNode *node = linkedQueueDeQueue(queue);
linkedQueueEnQueue(levelOrderList, node->data);
if (node->lNode != NULL) {
linkedQueueEnQueue(queue, node->lNode);
}
if (node->rNode != NULL) {
linkedQueueEnQueue(queue, node->rNode);
}
}
}
由二叉树的以下遍历序列组合可以唯一确认一棵二叉树:
BinaryTree binaryTreePreInOrderConstructor(void **preOrderList, void **inOrderList, int (*compare)(void *, void *), int ps, int pe, int is, int ie) {
BinaryTreeNode *root = malloc(sizeof(BinaryTreeNode));
root->data = *(preOrderList + ps);
int lLen, rLen;
for (int i = is; i <= ie; ++i) {
if (compare(*(inOrderList + i), *(preOrderList + ps))) {
lLen = i - is;
rLen = ie - i;
break;
}
}
if (lLen) {
root->lNode = binaryTreePreInOrderConstructor(preOrderList, inOrderList, compare, ps + 1, ps + lLen, is, is + lLen - 1);
} else {
root->lNode = NULL;
}
if (rLen) {
root->rNode = binaryTreePreInOrderConstructor(preOrderList, inOrderList, compare, pe - rLen + 1, pe, ie - rLen + 1, ie);
} else {
root->rNode = NULL;
}
return root;
}
如果将二叉树的某一遍历序列使用填充元素填充为满二叉树的相同遍历序列,那么也可以唯一确定一棵二叉树:
BiTree completeLevelOrderConstruct(BiTreeElemType completeLevelOrderList[],int len,BiTreeElemType filler){
LinkQueue queue= linkQueueConstruct();
BiTNode * root= malloc(sizeof (BiTNode));
root->data=completeLevelOrderList[0];
enQueue(queue,root);
for (int i = 1; !isEmpty(queue); ++i) {
BiTNode * node=deQueue(queue);
if(node!=NULL){
if((2*1-1)>=len||(2*i+1-1)>=len){
node->lChild=NULL;
node->rChild=NULL;
continue;
}
if(completeLevelOrderList[2*i-1]!=filler){
node->lChild= malloc(sizeof (BiTNode));
node->lChild->data=completeLevelOrderList[2*i-1];
enQueue(queue,node->lChild);
} else{
node->lChild=NULL;
enQueue(queue,NULL);
}
if(completeLevelOrderList[2*i+1-1]!=filler){
node->rChild= malloc(sizeof (BiTNode));
node->rChild->data=completeLevelOrderList[2*i+1-1];
enQueue(queue,node->rChild);
} else{
node->rChild=NULL;
enQueue(queue,NULL);
}
} else{
continue;
}
}
linkQueueFinalize(queue);
return root;
}
由于二叉树的结构简单、规律性最强,因此在处理树和森林时一般将它们转换成一个唯一的二叉树进行。由于树和二叉树都可以使用二叉链表表示,那么就可以以二叉链表做媒介来进行转化。
树转换为二叉树:每个结点左指针指向它的第一个孩子,右指针指向它在树中的相邻右兄弟,由于根节点没有兄弟,所以对应的二叉树没有右子树。
树转换为二叉树的画法:在兄弟结点之间加一条线;对每一个结点,只保留它与第一个孩子的连线,与其它孩子的连线全部抹掉;以树根为中心顺时针旋转四十五度。
森林转换为二叉树:先将森林中的每一棵树转换为二叉树,然后将第二棵二叉树作为第一棵二叉树的右子树,再将第三棵二叉树作为第二棵二叉树的右子树···依次类推。
森林转换为二叉树的画法:将森林中的每一棵树转换为二叉树;每棵树的根视为兄弟关系,在每棵树的根之间加一条连线;以第一棵树的根为中心顺时针旋转四十五度。
二叉树转换为森林:将二叉树的根及左子树视为第一棵树的二叉树形式,并将右链断开;将右子树视为一个由除第一棵树外的森林转换后的二叉树,应用相同的方法,直到最后只剩一棵没有右子树的二叉树为止;最后将每棵二叉树依次转换为树就得到了森林。
树的遍历方式如下:
森林的遍历方式如下:
每一层的结点数都达到最大值的二叉树称为满二叉树,满二叉树的性质如下:
一棵深度为 k k k的有 n n n个结点的二叉树,对树中的结点自上而下,自左到右从 1 1 1开始编号,如果编号为 i i i的结点与满二叉树中编号为 i i i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。完全二叉树的性质如下:
在含有 n n n个结点的二叉树中,含有 n + 1 n+1 n+1个空指针域。线索化是指利用这些空指针域存放指向结点的前驱和后继的指针,它的规则如下:
此外还需要增加两个标志位标识指针域是指向左右孩子还是指向前驱后继,最好再添加一个指针域指向其父结点。
struct ThreadedBinaryTreeNode {
void *data;
ThreadedBinaryTreeNode *lNode;
ThreadedBinaryTreeNode *rNode;
ThreadedBinaryTreeNode *pNode;
bool lIsPredecessor;
bool rIsSuccessor;
};
构造一棵线索二叉树分为两步:
ThreadTree
ThreadTree
转换成一个真正的线索二叉树以前序线索化为例,它的算法思想为:
tree
指针指向当前访问的结点,predecessor
指针指向tree的前驱tree
的左指针是否为空,若为空则将其指向predecessor
predecessor
的右指针是否为空,若为空则将其指向tree
/**
* 先序线索化
* @param tree
* @param predecessor
* @return
*/
ThreadedBinaryTree preOrderThreaded(ThreadedBinaryTree tree, ThreadedBinaryTreeNode *predecessor) {
if (tree != NULL) {
if (tree->lNode == NULL) {
tree->lNode = predecessor;
tree->lIsPredecessor = true;
}
if (predecessor != NULL && predecessor->rNode == NULL) {
predecessor->rNode = tree;
predecessor->rIsSuccessor = true;
}
predecessor = tree;
if (tree->lIsPredecessor == false) {
preOrderThreaded(tree->lNode, predecessor);
}
if (tree->rIsSuccessor == false) {
preOrderThreaded(tree->rNode, predecessor);
}
}
}
以前序线序哦二叉树为例,寻找tree
结点前驱的算法思想如下:
tree->lIsPredecessor == true
,则直接返回其左孩子tree
的左孩子不指向其前驱:
tree
是其父结点的左孩子,则直接返回其父结点tree
是其父结点的右孩子
/**
* 寻找前序序列的前驱
* @param tree
* @return
*/
ThreadedBinaryTreeNode *preOrderFirstNode(ThreadedBinaryTree tree) {
if (tree->lIsPredecessor == true) {
return tree->lNode;
} else {
ThreadedBinaryTreeNode *parent = tree->pNode;
if (parent->lNode == tree) {
return parent;
} else {
if (parent->lIsPredecessor == true || parent->lNode == NULL) {
return parent;
} else {
ThreadedBinaryTreeNode *node = parent->lNode;
while (node != NULL) {
if (node->rNode == tree) {
break;
} else if (node->rNode != NULL) {
node = node->rNode;
} else if (node->lIsPredecessor == false && node->lNode != NULL) {
node = node->lNode;
}
}
return node;
}
}
}
}
寻找后继的算法思想如下:
tree->rIsSuccessor == true
,则直接返回其右孩子tree
的右孩子不指向其后继:
tree->lIsPredecessor == false && tree->lNode != NULL
,返回其左孩子/**
* 寻找前序序列的后继
* @param tree
* @return
*/
ThreadedBinaryTreeNode *preOrderNextNode(ThreadedBinaryTree tree) {
if (tree->rIsSuccessor == true) {
return tree->rNode;
} else {
if (tree->lIsPredecessor == false && tree->lNode != NULL) {
return tree->lNode;
} else {
return tree->rNode;
}
}
以前序线序哦二叉树为例,前序线索二叉树中包含了二叉树的前驱和后继的信息,在对其进行遍历时,只需要找到前序序列中的第一个结点,然后依次找结点的后继,直至其后继为空。
/**
* 线索二叉树前序遍历
* @param tree
* @param queue
*/
void threadedBinaryTreePreOrder(ThreadedBinaryTree tree, LinkedQueue queue) {
for (ThreadedBinaryTreeNode *node = tree; node != NULL; node = preOrderNextNode(node)) {
linkedQueueEnQueue(queue, node->data);
}
}
如果给树中结点赋一个有某种含义的数值,则这个数值称为该结点的权。结点的权和根结点到该结点的路径长度的乘积称为结点的带权路径长度,树的带权路径长度是树中所有叶子节点带权路径长度之和。带权路径长度最小的二叉树称为最优二叉树。最优二叉树的特点如下:
struct HuffmanTreeNode {
void *data;
void *weight;
struct HuffmanTreeNode *lNode;
struct HuffmanTreeNode *rNode;
};
给定 n n n个结点以及它们的权值,那么构造过程如下:
/**
* 构造哈夫曼树
* @param dataList 数据列表
* @param weightList 权值列表
* @param size 大小
* @param reCompare 权值逆序比较
* @param weightSum 权值相加
* @return
*/
HuffmanTree huffmanTreeNodeConstructor(void *dataList[], void *weightList[], int size, int (*reCompare)(void *, void *), void *(*weightSum)(void *, void *)) {
HuffmanTreeNode *forest[size];
int treeCount = 0;
for (int i = 0; i < size; ++i) {
HuffmanTreeNode *node = malloc(sizeof(HuffmanTreeNode));
node->data = dataList[i];
node->weight = weightList[i];
node->lNode = NULL;
node->rNode = NULL;
forest[i] = node;
treeCount++;
}
sorted(forest, size, reCompare);
for (; treeCount != 1;) {
HuffmanTreeNode *node = malloc(sizeof(HuffmanTreeNode));
node->lNode = forest[0];
forest[0] = NULL;
treeCount--;
node->rNode = forest[1];
forest[1] = NULL;
treeCount--;
node->data = NULL;
node->weight = weightSum(node->lNode->weight, node->rNode->weight);
forest[0] = node;
sorted(forest, size, reCompare);
}
return forest[0];
}
由哈夫曼树可以得到哈夫曼编码:
/**
* 是否是叶子结点
* @param node
* @return
*/
static bool isLeafNode(HuffmanTreeNode *node) {
return node->lNode == NULL && node->rNode == NULL;
}
/**
* 哈夫曼编码
* @param tree
* @param target
* @param compare
* @return
*/
char *huffmanCoding(HuffmanTree tree, void *target, int (*compare)(void *, void *)) {
if (tree != NULL) {
char *lr = huffmanCoding(tree->lNode, target, compare);
char *rr = huffmanCoding(tree->rNode, target, compare);
if (lr != NULL) {
char *code = calloc(strlen(lr) + 2, sizeof(char));
strcat(code, "0");
strcat(code, lr);
return code;
} else if (rr != NULL) {
char *code = calloc(strlen(rr) + 2, sizeof(char));
strcat(code, "1");
strcat(code, rr);
return code;
} else if (tree->lNode != NULL && isLeafNode(tree->lNode) && compare(tree->lNode->data, target) == 0) {
return "0";
} else if (tree->rNode != NULL && isLeafNode(tree->rNode) && compare(tree->rNode->data, target) == 0) {
return "1";
} else {
return NULL;
}
} else {
return NULL;
}
}
二叉排序树是满足以下性质的二叉树:
对二叉排序树进行中序遍历可以得到一个递增有序的有序序列。
struct SortedBinaryTreeNode {
void *data;
SortedBinaryTreeNode *lNode;
SortedBinaryTreeNode *rNode;
};
算法思想:
/**
* 二叉排序树的插入
* @param tree
* @param element
* @param compare
* @return
*/
bool sortedBinaryTreeInsert(SortedBinaryTree *tree, void *element, int (*compare)(void *, void *)) {
if ((*tree) == NULL) {
*tree = malloc(sizeof(SortedBinaryTreeNode));
(*tree)->data = element;
(*tree)->lNode = NULL;
(*tree)->rNode = NULL;
return true;
} else {
int cmpResult = compare(element, (*tree)->data);
if (cmpResult == 0) {
return false;
} else if (cmpResult > 0) {
if ((*tree)->rNode == NULL) {
SortedBinaryTreeNode *node = malloc(sizeof(SortedBinaryTreeNode));
node->data = element;
node->rNode = NULL;
node->rNode = NULL;
(*tree)->rNode = node;
return true;
} else {
return sortedBinaryTreeInsert(&(*tree)->rNode, element, compare);
}
} else {
if ((*tree)->lNode == NULL) {
SortedBinaryTreeNode *node = malloc(sizeof(SortedBinaryTreeNode));
node->data = element;
node->rNode = NULL;
node->rNode = NULL;
(*tree)->lNode = node;
return true;
} else {
return sortedBinaryTreeInsert(&(*tree)->lNode, element, compare);
}
}
}
}
/**
* 二叉排序树构造函数
* @param elementList
* @param size
* @param compare
* @return
*/
SortedBinaryTree sortedBinaryTreeConstructor(void **elementList, int size, int (*compare)(void *, void *)) {
static SortedBinaryTree tree = NULL;
for (int i = 0; i < size; ++i) {
sortedBinaryTreeInsert(&tree, *(elementList + i), compare);
}
return tree;
}
算法思想:
/**
* 二叉排序树的查找
* @param tree
* @param element
* @param compare
* @return
*/
SortedBinaryTreeNode *sortedBinaryTreeSearch(SortedBinaryTree tree, void *element, int (*compare)(void *, void *)) {
if (tree == NULL) {
return NULL;
} else {
int cmpResult = compare(element, tree->data);
if (cmpResult == 0) {
return tree;
} else if (cmpResult > 0) {
return sortedBinaryTreeSearch(tree->rNode, element, compare);
} else {
return sortedBinaryTreeSearch(tree->lNode, element, compare);
}
}
}
算法思想:
/**
* 删除元素
* @param tree
* @param element
* @param compare
* @return
*/
bool sortedBinaryTreeDelete(SortedBinaryTree *tree, void *element, int (*compare)(void *, void *)) {
if (*tree == NULL) {
return false;
} else {
int cmpResult = compare(element, (*tree)->data);
if (cmpResult == 0) {
if ((*tree)->lNode == NULL && (*tree)->rNode == NULL) {
free(*tree);
*tree = NULL;
return true;
} else if ((*tree)->lNode == NULL) {
SortedBinaryTreeNode *node = (*tree);
*tree = (*tree)->rNode;
free(node);
return true;
} else if ((*tree)->rNode == NULL) {
SortedBinaryTreeNode *node = (*tree);
*tree = (*tree)->lNode;
free(node);
return true;
} else {
SortedBinaryTreeNode *node = (*tree)->rNode;
while (node->lNode != NULL) {
node = node->lNode;
}
(*tree)->data = node->data;
return sortedBinaryTreeDelete(&(*tree)->rNode, node->data, compare);
}
} else if (cmpResult > 0) {
return sortedBinaryTreeDelete(&(*tree)->rNode, element, compare);
} else {
return sortedBinaryTreeDelete(&(*tree)->lNode, element, compare);
}
}
}
结点的平衡因子定义如下:
平衡因子 = 左子树高 − 右子树高 平衡因子=左子树高-右子树高 平衡因子=左子树高−右子树高
树中任一结点的平衡因子的绝对值不超过 1 1 1的二叉树称为平衡二叉树,平衡二叉树避免了树高的过度增长,如果可以保证一棵二叉排序树为平衡二叉树,那么就可以减少搜索元素时比较的次数,从而提高二叉排序树的性能。
struct BalancedBinaryTreeNode {
void *data;
BalancedBinaryTreeNode *lNode;
BalancedBinaryTreeNode *rNode;
};
当对一棵平衡二叉树进行插入或删除操作时,有可能会导致平衡二叉树失衡,此时应该从插入或删除结点往回找到第一个不平衡结点,调整以该结点为根的子树,这棵被调整的树称为最小不平衡子树。平衡二叉树的失衡类型有以下四种,针对不同的类型有不同的调整策略。
void ll(BalancedBinaryTree *tree) {
BalancedBinaryTreeNode *rootNode = *tree;
BalancedBinaryTreeNode *lNode = rootNode->lNode;
BalancedBinaryTreeNode *lrNode = lNode->rNode;
*tree = lNode;
lNode->rNode = rootNode;
rootNode->lNode = lrNode;
}
void rr(BalancedBinaryTree *tree) {
BalancedBinaryTreeNode *rootNode = *tree;
BalancedBinaryTreeNode *rNode = rootNode->rNode;
BalancedBinaryTreeNode *rlNode = rNode->lNode;
*tree = rNode;
rNode->lNode = rootNode;
rootNode->rNode = rlNode;
}
void lr(BalancedBinaryTree *tree) {
BalancedBinaryTreeNode *rootNode = *tree;
BalancedBinaryTreeNode *lNode = rootNode->lNode;
BalancedBinaryTreeNode *lrNode = lNode->rNode;
BalancedBinaryTreeNode *lrlNode = lrNode->lNode;
BalancedBinaryTreeNode *lrrNode = lrNode->rNode;
*tree = lrNode;
lrNode->lNode = lNode;
lrNode->rNode = rootNode;
lNode->rNode = lrlNode;
rootNode->lNode = lrrNode;
}
void rl(BalancedBinaryTree *tree) {
BalancedBinaryTreeNode *rootNode = *tree;
BalancedBinaryTreeNode *rNode = rootNode->rNode;
BalancedBinaryTreeNode *rlNode = rNode->lNode;
BalancedBinaryTreeNode *rllNode = rlNode->lNode;
BalancedBinaryTreeNode *rlrNode = rlNode->rNode;
*tree = rlNode;
rlNode->lNode = rootNode;
rlNode->rNode = rNode;
rNode->lNode = rlrNode;
rootNode->rNode = rllNode;
}
最后是失衡调整算法,在这个算法中只需要判断失衡类型是哪种并调用相应的算法即可:
void imbalanceAdjust(BalancedBinaryTree *tree) {
int lDeep = balancedBinaryTreeDeep((*tree)->lNode);
int rDeep = balancedBinaryTreeDeep((*tree)->rNode);
int balance = lDeep - rDeep;
if (balance > 1) {
lDeep = balancedBinaryTreeDeep((*tree)->lNode->lNode);
rDeep = balancedBinaryTreeDeep((*tree)->lNode->rNode);
//左左和左右
if (lDeep > rDeep) {
ll(tree);
} else {
lr(tree);
}
} else if (balance < -1) {
lDeep = balancedBinaryTreeDeep((*tree)->rNode->lNode);
rDeep = balancedBinaryTreeDeep((*tree)->rNode->rNode);
//右右和右左
if (rDeep > lDeep) {
rr(tree);
} else {
rl(tree);
}
} else {
return;
}
}
对于平衡二叉树的插入而言,只要调整第一棵最小不平衡子树,只要第一颗最小不平衡子树恢复了平衡,那么整棵树就恢复了平衡。
/**
* 二叉排序树的插入
* @param tree
* @param element
* @param compare
* @return
*/
bool balancedBinaryTreeInsert(BalancedBinaryTree *tree, void *element, int (*compare)(void *, void *)) {
if ((*tree) == NULL) {
*tree = malloc(sizeof(BalancedBinaryTreeNode));
(*tree)->data = element;
(*tree)->lNode = NULL;
(*tree)->rNode = NULL;
return true;
} else {
int cmpResult = compare(element, (*tree)->data);
if (cmpResult == 0) {
return false;
} else if (cmpResult > 0) {
if ((*tree)->rNode == NULL) {
BalancedBinaryTreeNode *node = malloc(sizeof(BalancedBinaryTreeNode));
node->data = element;
node->rNode = NULL;
node->rNode = NULL;
(*tree)->rNode = node;
return true;
} else {
bool result = balancedBinaryTreeInsert(&(*tree)->rNode, element, compare);
imbalanceAdjust(tree);
return result;
}
} else {
if ((*tree)->lNode == NULL) {
BalancedBinaryTreeNode *node = malloc(sizeof(BalancedBinaryTreeNode));
node->data = element;
node->rNode = NULL;
node->rNode = NULL;
(*tree)->lNode = node;
return true;
} else {
bool result = balancedBinaryTreeInsert(&(*tree)->lNode, element, compare);
imbalanceAdjust(tree);
return result;
}
}
}
}
对于平衡二叉树的删除而言,需要在被删除的结点处一直向上回溯,找到所有最小不平衡子树并进行失衡调整。
/**
* 删除元素
* @param tree
* @param element
* @param compare
* @return
*/
bool balancedBinaryTreeDelete(BalancedBinaryTree *tree, void *element, int (*compare)(void *, void *)) {
if (*tree == NULL) {
return false;
} else {
int cmpResult = compare(element, (*tree)->data);
if (cmpResult == 0) {
if ((*tree)->lNode == NULL && (*tree)->rNode == NULL) {
free(*tree);
*tree = NULL;
return true;
} else if ((*tree)->lNode == NULL) {
BalancedBinaryTreeNode *node = (*tree);
*tree = (*tree)->rNode;
free(node);
return true;
} else if ((*tree)->rNode == NULL) {
BalancedBinaryTreeNode *node = (*tree);
*tree = (*tree)->lNode;
free(node);
return true;
} else {
BalancedBinaryTreeNode *node = (*tree)->rNode;
while (node->lNode != NULL) {
node = node->lNode;
}
(*tree)->data = node->data;
bool result = balancedBinaryTreeDelete(&(*tree)->rNode, node->data, compare);
imbalanceAdjust(tree);
return result;
}
} else if (cmpResult > 0) {
bool result = balancedBinaryTreeDelete(&(*tree)->rNode, element, compare);
imbalanceAdjust(tree);
return result;
} else {
bool result = balancedBinaryTreeDelete(&(*tree)->lNode, element, compare);
imbalanceAdjust(tree);
return result;
}
}
}
虽然平衡二叉树可以保证二叉排序树的性能,但是频繁的平衡调整代价较大,为此在平衡二叉树上放宽了条件,引入了红黑树,红黑树是具有以下性质的二叉排序树:
struct RedBlackTreeNode {
void *data;
RedBlackTreeNode *lNode;
RedBlackTreeNode *rNode;
RedBlackTreeNode *pNode;
int color;
};
红黑树的性质如下:
红黑树插入的算法思想如下:①先查找,确定插入的位置(和二叉搜索树相同)。②插入结点是根结点则染成黑色,否则就染成红色。③如果插入新结点后满足红黑树的定义,则插入结束,否则就进行调整(此时只可能违反红黑树的第四条性质)。针对不同的类型有不同的调整策略:
static void ll(RedBlackTree *tree) {
RedBlackTreeNode *rootNode = *tree;
RedBlackTreeNode *rootPNode = (*tree)->pNode;
RedBlackTreeNode *lNode = rootNode->lNode;
RedBlackTreeNode *lrNode = lNode->rNode;
*tree = lNode;
lNode->pNode = rootPNode;
lNode->rNode = rootNode;
rootNode->pNode = lNode;
rootNode->lNode = lrNode;
if (lrNode != NULL) {
lrNode->pNode = rootNode;
}
reverseColor(rootNode);
reverseColor(lNode);
}
static void rr(RedBlackTree *tree) {
RedBlackTreeNode *rootNode = *tree;
RedBlackTreeNode *rootPNode = (*tree)->pNode;
RedBlackTreeNode *rNode = rootNode->rNode;
RedBlackTreeNode *rlNode = rNode->lNode;
*tree = rNode;
rNode->pNode = rootPNode;
rNode->lNode = rootNode;
rootNode->pNode = rNode;
rootNode->rNode = rlNode;
if (rlNode != NULL) {
rlNode->pNode = rootNode;
}
reverseColor(rootNode);
reverseColor(rNode);
}
static void lr(RedBlackTree *tree) {
RedBlackTreeNode *rootNode = *tree;
RedBlackTreeNode *rootPNode = (*tree)->pNode;
RedBlackTreeNode *lNode = rootNode->lNode;
RedBlackTreeNode *lrNode = lNode->rNode;
RedBlackTreeNode *lrlNode = lrNode->lNode;
RedBlackTreeNode *lrrNode = lrNode->rNode;
*tree = lrNode;
lrNode->pNode = rootPNode;
lrNode->lNode = lNode;
lNode->pNode = lrNode;
lrNode->rNode = rootNode;
rootNode->pNode = lrNode;
lNode->rNode = lrlNode;
if (lrlNode != NULL) {
lrlNode->pNode = lNode;
}
rootNode->lNode = lrrNode;
if (lrrNode != NULL) {
lrrNode->pNode = rootNode;
}
reverseColor(rootNode);
reverseColor(lrNode);
}
static void rl(RedBlackTree *tree) {
RedBlackTreeNode *rootNode = *tree;
RedBlackTreeNode *rootPNode = (*tree)->pNode;
RedBlackTreeNode *rNode = rootNode->rNode;
RedBlackTreeNode *rlNode = rNode->lNode;
RedBlackTreeNode *rllNode = rlNode->lNode;
RedBlackTreeNode *rlrNode = rlNode->rNode;
*tree = rlNode;
rlNode->pNode = rootPNode;
rlNode->lNode = rootNode;
rootNode->pNode = rlNode;
rlNode->rNode = rNode;
rNode->pNode = rlNode;
rNode->lNode = rlrNode;
if (rlrNode != NULL) {
rlrNode->pNode = rNode;
}
rootNode->rNode = rllNode;
if (rllNode != NULL) {
rllNode->pNode = rootNode;
}
reverseColor(rootNode);
reverseColor(rlNode);
}
完整的失衡调整代码如下:
static void imbalanceAdjust(RedBlackTree tree, RedBlackTree *root) {
RedBlackTreeNode *fatherNode = tree->pNode;
if (fatherNode == NULL) {
return;
}
RedBlackTreeNode *grandFatherNode = fatherNode->pNode;
if (grandFatherNode == NULL) {
return;
}
RedBlackTreeNode *uncleNode = grandFatherNode->lNode == fatherNode ? grandFatherNode->rNode : grandFatherNode->lNode;
if (!(tree->color == RED && fatherNode->color == RED)) {
return;
}
if (uncleNode == NULL || uncleNode->color == BLACK) {
if (grandFatherNode->lNode == fatherNode) {
if (fatherNode->lNode == tree) {
if (grandFatherNode->pNode == NULL) {
ll(root);
} else {
ll(grandFatherNode->pNode->lNode == grandFatherNode ? &(grandFatherNode->pNode->lNode) : &(grandFatherNode->pNode->rNode));
}
} else {
if (grandFatherNode->pNode == NULL) {
lr(root);
} else {
lr(grandFatherNode->pNode->lNode == grandFatherNode ? &(grandFatherNode->pNode->lNode) : &(grandFatherNode->pNode->rNode));
}
}
} else {
if (fatherNode->rNode == tree) {
if (grandFatherNode->pNode == NULL) {
rr(root);
} else {
rr(grandFatherNode->pNode->lNode == grandFatherNode ? &(grandFatherNode->pNode->lNode) : &(grandFatherNode->pNode->rNode));
}
} else {
if (grandFatherNode->pNode == NULL) {
rl(root);
} else {
rl(grandFatherNode->pNode->lNode == grandFatherNode ? &(grandFatherNode->pNode->lNode) : &(grandFatherNode->pNode->rNode));
}
}
}
} else {
reverseColor(grandFatherNode);
reverseColor(fatherNode);
reverseColor(uncleNode);
if (grandFatherNode->pNode == NULL) {
grandFatherNode->color = BLACK;
} else {
imbalanceAdjust(grandFatherNode, root);
}
}
}
完整的插入代码如下:
static bool insert(RedBlackTree *tree, RedBlackTree *root, void *element, int (*compare)(void *, void *)) {
if ((*tree) == NULL) {
*tree = malloc(sizeof(RedBlackTreeNode));
(*tree)->data = element;
(*tree)->lNode = NULL;
(*tree)->rNode = NULL;
(*tree)->pNode = NULL;
(*tree)->color = BLACK;
return true;
} else {
int cmpResult = compare(element, (*tree)->data);
if (cmpResult == 0) {
return false;
} else if (cmpResult > 0) {
if ((*tree)->rNode == NULL) {
RedBlackTreeNode *node = malloc(sizeof(RedBlackTreeNode));
node->data = element;
node->rNode = NULL;
node->rNode = NULL;
node->pNode = *tree;
node->color = RED;
(*tree)->rNode = node;
imbalanceAdjust(node, root);
return true;
} else {
return insert(&(*tree)->rNode, root, element, compare);
}
} else {
if ((*tree)->lNode == NULL) {
RedBlackTreeNode *node = malloc(sizeof(RedBlackTreeNode));
node->data = element;
node->rNode = NULL;
node->rNode = NULL;
node->pNode = *tree;
node->color = RED;
(*tree)->lNode = node;
imbalanceAdjust(node, root);
return true;
} else {
return insert(&(*tree)->lNode, root, element, compare);
}
}
}
}
/**
* 二叉排序树的插入
* @param tree
* @param element
* @param compare
* @return
*/
bool redBlackTreeInsert(RedBlackTree *tree, void *element, int (*compare)(void *, void *)) {
return insert(tree, tree, element, compare);
}
B树又称为多路平衡查找树,B数中所有结点的孩子个数的最大值称为B树的阶,通常用 m m m表示,B树要么是空树,要么是满足以下条件的 m m m叉树:
struct BTreeNode {
void **dataList;
BTreeNode **childList;
int dataCount;
};