#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode{
int height;
// 在结构体中添加一个高度成员变量,以在每次插入时候都可以检查相对根节点的是否平衡
int data;
struct TreeNode * lchild;
struct TreeNode * rchild;
}TreeNode;
int get_height(TreeNode * node){
// 之所以需要这个函数,是因为避免调node->height结果出现空指针异常
// 在每一次插入后,相对根节点都需要更新它的高度值
return node?node->height:0;
// 如果结点为空则高度值为0,在最下面的结点的高度值都为1
}
int get_max(int a, int b){
return a>b? a: b;
// 在每一次插入后,相对根节点都需要更新它的高度值,当然递归返回之后,你也不知道插入的元素跑下
// 面哪去了,所以更新相对根节点的高度值时候,找左右儿子的高度差,一旦达到2,就说明刚刚那次插入
// 使这个局部位置失衡了,那从相对根节点开始往下看,无非三个结点需要重新移动位置
// 局部失衡有几种情况呢?
// 四种,三个结点位置从上往下看分别是
// 1. a 2. a 3. a 4. a
// b b b b
// c c c c
// 说说调整思路
// 1 和 3很简单,分别调整一次就可以,调整的方式就是旋转,分别是顺时针旋转和逆时针旋转
// 1 直接顺指针转 转成这样 b 3 直接逆时针旋转 转成这样 b
// c a a c
// ok,顺便说一下这两个旋转的代码
}
void rr_rotation(TreeNode* node, TreeNode** root) {
/**
* a
* b
* c
*/
// 这就是逆时针旋转,需要传入的参数当然是这个小局部的相对根结点,相对上图情况就是a结点的位置
// 所以需传入存放a结点位置的位置,因为要改变这个位置的内容,使相对根节点得到真正的改变,root
// 当然还要传入a结点的位置,通过该位置(指针)改变a结点结构体中的左右子指针以及高度,node
*root = node->rchild;
// 原相对根节点位置存放的是a的位置,现在直接改成b的位置,b正式成为这个局部的相对根节点
node->rchild = NULL;
// a的右儿子本来是b,现在当然不是了,a要去做b的儿子了,a的左儿子本来就一定为空,故不画蛇添足
// 但右儿子需重新赋值为0
node->height = 1;
// 高度当然,本来为3,但现在为1
// 多提一嘴,新的根节点b的高度这不用设置,因为每次插入函数的最末尾都会更新高度
// 在更新高度前,会检验是否出现失衡,当然这里的函数会在插入函数的中间位置一旦发现失衡才会调用
// 所以调用完,相对根结点的高度一定会更新,这就不重复了!
(*root)->lchild = node;
// ok,a变成b的儿子,c其实什么都没改变(这也是后续两步调整中的铺垫)
// 经过调整变成,解决了这个局部的失衡
/**
* b
* a c
*/
}
void ll_rotation(TreeNode* node, TreeNode** root) {
// 大同小异!
/**
* a
* b
* c
*/
*root = node->lchild;
node->lchild = NULL;
node->height = 1;
(*root)->rchild = node;
/**
* b
* c a
*/
}
// 这里说说这种情况: a
// b
// c
// 这种情况就要分两步调整,首先,先以b为相对根节点,对 bc这个局部进行调整,你可以假设c有个左孩子
// 实际这样假设对调整结果当然没有影响,但这样可以直接对bc这个局部进行ll调整,
// 经过调整: a
// c
// b
// 好了简单,第二步,以a为相对根节点看这个局部,直接rr旋转调整
// 最终结果:
// c
// a b
// 解决了这种情况的失衡,当然
// 情况如 a
// b
// c
// 先以b为局部根节点,对bc这个局部进行rr旋转调整,然后再以a为相对根进行ll旋转调整
// ok,做完所有铺垫,来到了真正的avl插入,我总结为边插入,边检查,检查到了就转转
void avl_insert(TreeNode ** root, int data){
if((*root) == NULL){
// 碰到空位置插入,很好理解
(*root) = (TreeNode *) malloc(sizeof(TreeNode));
(*root)->data = data;
(*root)->lchild = NULL;
(*root)->rchild = NULL;
// 赋空,当然也是为后来的元素插入留位置,毕竟,看到空位置才插入
(*root)->height = 1;
// 一旦屁股坐定(一定是在最下端,当然高度是1)
}else{
if((*root)->data < data){
// 这就是排序树的插入规则了,比根节点大的数据,往右找空位,反之亦然
avl_insert(&((*root)->rchild), data);
// 利用递归去寻找位置并插入,接下来就是插完之后必须检查了!
int r_height = get_height((*root)->rchild);
int l_height = get_height((*root)->lchild);
if(r_height - l_height == 2){
// 左右高度相差2时,必须调整失衡
if(data < (*root)->rchild->data)
// 这种情况: a
// b
// c
{
// 简单,先以b为相对根对bc局部进行ll旋转,再对abc以a为相对根进行rr旋转
ll_rotation((*root)->rchild, &((*root)->rchild));
rr_rotation((*root), &(*root));
}else{
// 这种就是简单的rr旋转了
rr_rotation((*root), &(*root));
}
}
}else if((*root)->data > data){
// 下面的当然是ll或者先rr再ll两步走
avl_insert(&((*root)->lchild), data);
int r_height = get_height((*root)->rchild);
int l_height = get_height((*root)->lchild);
if(l_height - r_height == 2){
if(data > (*root)->lchild->data)
{
rr_rotation((*root)->lchild, &((*root)->lchild));
ll_rotation((*root), &(*root));
}else{
ll_rotation((*root), &(*root));
}
}
}
// 很帅也很关键的一步,每一次插入,虽然每次插入后要检查平衡,必要时调整
// 高度当然也要进行更新,每次插入都要更新!会以递归的形式进行更新,试着调试一下
// 逻辑美如画~
(*root)->height = get_max(get_height((*root)->lchild), get_height((*root)->rchild)) + 1;
}
}
void pre_order(TreeNode * node){
// 先序遍历
if(node == NULL)
return;
printf("%d ", node->data);
pre_order(node->lchild);
pre_order(node->rchild);
}
typedef struct Queue{
// 层次遍历需要一个先进先出的队列
TreeNode *data;
struct Queue * next;
}Queue;
Queue * init_queue(){
Queue * head = (Queue *) malloc(sizeof(Queue));
head->data = NULL;
head->next = NULL;
}
int get_length(Queue * head){
int length = 0;
head = head->next;
while (head){
length++;
head = head->next;
}
return length;
}
void en_queue(Queue * head, TreeNode * node){
Queue * last_q_node = head;
int length = get_length(head);
for (int i = 0; i < length; ++i) {
last_q_node = last_q_node->next;
}
Queue * en_q_node = (Queue *) malloc(sizeof(Queue));
en_q_node->data = node;
last_q_node->next = en_q_node;
en_q_node->next = NULL;
}
TreeNode * de_queue(Queue * head){
Queue * de_q_node = head->next;
TreeNode * de_q_node_data = de_q_node->data;
head->next = de_q_node->next;
free(de_q_node);
return de_q_node_data;
}
void print_queue(Queue * head){
int length = get_length(head);
printf("head, front --- back\n");
head = head->next;
for (int i = 0; i < length; ++i) {
printf("%d ",head->data->data);
head = head->next;
}
printf("\n");
}
void level_traversal(TreeNode * root){
Queue * head = init_queue();
en_queue(head, root);
while(get_length(head) != 0){
TreeNode * de_node = de_queue(head);
printf("%d, height = %d\n",de_node->data, de_node->height);
if(de_node->lchild){
en_queue(head, de_node->lchild);
}
if(de_node->rchild){
en_queue(head, de_node->rchild);
}
}
}
int main() {
int num[5] = {5,3,4,1,6}; // lr rr+ll
/**
* 5
* 3
* 4
* rr
* 5
* 4
* 3
*
* 4
* 3 5
* 1 6
*/
TreeNode * root = NULL;
for (int i = 0; i < 5; ++i) {
avl_insert(&root, num[i]);
}
level_traversal(root);
// 层次遍历之后应该是: 4 3 5 1 6
return 0;
}
运行结果:
~