243 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * Copyright (c) 2006-2022, RT-Thread Development Team
 | |
|  *
 | |
|  * SPDX-License-Identifier: Apache-2.0
 | |
|  *
 | |
|  * Change Logs:
 | |
|  * Date           Author       Notes
 | |
|  * 2019-10-12     Jesven       first version
 | |
|  * 2022-11-14     WangXiaoyao  Optimize footprint and performance
 | |
|  *                             Export as ADT for generic use case
 | |
|  */
 | |
| #include <stddef.h>
 | |
| #include <stdint.h>
 | |
| 
 | |
| #include "avl.h"
 | |
| 
 | |
| #define HEIGHT_OF(node) ((node) ? (node)->height : 0)
 | |
| #define IS_RCHILD(node) (!((node) - ((node)->parent->avl_right)))
 | |
| #define IS_LCHILD(node) (!((node) - ((node)->parent->avl_left)))
 | |
| #define NODE_PLACE(node)                                                       \
 | |
|     IS_LCHILD(node) ? &(node)->parent->avl_left : &(node)->parent->avl_right
 | |
| 
 | |
| static inline void rotate_right(struct util_avl_struct *axis,
 | |
|                                 struct util_avl_struct *lchild,
 | |
|                                 struct util_avl_struct *lrchild,
 | |
|                                 struct util_avl_struct **nodeplace,
 | |
|                                 size_t lrheight)
 | |
| {
 | |
|     axis->avl_left = lrchild;
 | |
|     lchild->avl_right = axis;
 | |
| 
 | |
|     axis->height = lrheight + 1;
 | |
|     lchild->height = axis->height + 1;
 | |
| 
 | |
|     lchild->parent = axis->parent;
 | |
|     axis->parent = lchild;
 | |
| 
 | |
|     *nodeplace = lchild;
 | |
|     if (lrchild != NULL)
 | |
|         lrchild->parent = axis;
 | |
| }
 | |
| 
 | |
| static inline void midmount_right(struct util_avl_struct *axis,
 | |
|                                   struct util_avl_struct *lchild,
 | |
|                                   struct util_avl_struct *lrchild,
 | |
|                                   struct util_avl_struct **nodeplace,
 | |
|                                   size_t lrheight)
 | |
| {
 | |
|     lchild->avl_right = lrchild->avl_left;
 | |
|     axis->avl_left = lrchild->avl_right;
 | |
|     lrchild->avl_left = lchild;
 | |
|     lrchild->avl_right = axis;
 | |
| 
 | |
|     lrchild->height = lchild->height;
 | |
|     lchild->height = lrheight;
 | |
|     axis->height = lrheight;
 | |
| 
 | |
|     lrchild->parent = axis->parent;
 | |
|     lchild->parent = lrchild;
 | |
|     axis->parent = lrchild;
 | |
|     if (lchild->avl_right != NULL)
 | |
|         lchild->avl_right->parent = lchild;
 | |
|     if (axis->avl_left != NULL)
 | |
|         axis->avl_left->parent = axis;
 | |
|     *nodeplace = lrchild;
 | |
| }
 | |
| 
 | |
| static inline void rotate_left(struct util_avl_struct *axis,
 | |
|                                struct util_avl_struct *rchild,
 | |
|                                struct util_avl_struct *rlchild,
 | |
|                                struct util_avl_struct **nodeplace,
 | |
|                                size_t rlheight)
 | |
| {
 | |
|     axis->avl_right = rlchild;
 | |
|     rchild->avl_left = axis;
 | |
| 
 | |
|     axis->height = rlheight + 1;
 | |
|     rchild->height = axis->height + 1;
 | |
| 
 | |
|     rchild->parent = axis->parent;
 | |
|     axis->parent = rchild;
 | |
| 
 | |
|     *nodeplace = rchild;
 | |
|     if (rlchild != NULL)
 | |
|         rlchild->parent = axis;
 | |
| }
 | |
| 
 | |
| static inline void midmount_left(struct util_avl_struct *axis,
 | |
|                                  struct util_avl_struct *rchild,
 | |
|                                  struct util_avl_struct *rlchild,
 | |
|                                  struct util_avl_struct **nodeplace,
 | |
|                                  size_t rlheight)
 | |
| {
 | |
|     rchild->avl_left = rlchild->avl_right;
 | |
|     axis->avl_right = rlchild->avl_left;
 | |
|     rlchild->avl_right = rchild;
 | |
|     rlchild->avl_left = axis;
 | |
| 
 | |
|     rlchild->height = rchild->height;
 | |
|     rchild->height = rlheight;
 | |
|     axis->height = rlheight;
 | |
| 
 | |
|     rlchild->parent = axis->parent;
 | |
|     rchild->parent = rlchild;
 | |
|     axis->parent = rlchild;
 | |
|     if (rchild->avl_left != NULL)
 | |
|         rchild->avl_left->parent = rchild;
 | |
|     if (axis->avl_right != NULL)
 | |
|         axis->avl_right->parent = axis;
 | |
| 
 | |
|     *nodeplace = rlchild;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief avl insertion & delete conceptually contain 2 stage
 | |
|  * 1. insertion/delete of reference
 | |
|  * 2. rebalance
 | |
|  */
 | |
| 
 | |
| void util_avl_rebalance(struct util_avl_struct *node,
 | |
|                         struct util_avl_root *root)
 | |
| {
 | |
|     if (!node)
 | |
|         return;
 | |
| 
 | |
|     struct util_avl_struct *axis = node;
 | |
|     struct util_avl_struct **nodeplace;
 | |
|     do
 | |
|     {
 | |
|         struct util_avl_struct *lchild = axis->avl_left;
 | |
|         struct util_avl_struct *rchild = axis->avl_right;
 | |
|         nodeplace = axis->parent ? NODE_PLACE(axis) : &root->root_node;
 | |
|         int lheight = HEIGHT_OF(lchild);
 | |
|         int rheight = HEIGHT_OF(rchild);
 | |
|         if (rheight + 1 < lheight)
 | |
|         {
 | |
|             struct util_avl_struct *lrchild = lchild->avl_right;
 | |
|             size_t lrheight = HEIGHT_OF(lrchild);
 | |
|             if (HEIGHT_OF(lchild->avl_left) >= lrheight)
 | |
|             {
 | |
|                 rotate_right(axis, lchild, lrchild, nodeplace, lrheight);
 | |
|                 axis = lchild->parent;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 midmount_right(axis, lchild, lrchild, nodeplace, lrheight);
 | |
|                 axis = lrchild->parent;
 | |
|             }
 | |
|         }
 | |
|         else if (lheight + 1 < rheight)
 | |
|         {
 | |
|             struct util_avl_struct *rlchild = rchild->avl_left;
 | |
|             size_t rlheight = HEIGHT_OF(rlchild);
 | |
|             if (HEIGHT_OF(rchild->avl_right) >= rlheight)
 | |
|             {
 | |
|                 rotate_left(axis, rchild, rlchild, nodeplace, rlheight);
 | |
|                 axis = rchild->parent;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 midmount_left(axis, rchild, rlchild, nodeplace, rlheight);
 | |
|                 axis = rlchild->parent;
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             int height = (lheight < rheight ? rheight : lheight) + 1;
 | |
|             if (height == axis->height)
 | |
|                 break;
 | |
|             axis->height = height;
 | |
|             axis = axis->parent;
 | |
|         }
 | |
|     } while (axis);
 | |
| }
 | |
| 
 | |
| void util_avl_remove(struct util_avl_struct *node, struct util_avl_root *root)
 | |
| {
 | |
|     struct util_avl_struct **nodeplace;
 | |
| 
 | |
|     if (root->root_node == NULL)
 | |
|         return;
 | |
| 
 | |
|     if (node->parent != NULL)
 | |
|     {
 | |
|         nodeplace = NODE_PLACE(node);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         nodeplace = &root->root_node;
 | |
|     }
 | |
| 
 | |
|     /* deletion */
 | |
|     if (node->avl_right == NULL)
 | |
|     {
 | |
|         *nodeplace = node->avl_left;
 | |
|         if (node->avl_left != NULL)
 | |
|             node->avl_left->parent = node->parent;
 | |
|         node = node->parent;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         struct util_avl_struct *rchild = node->avl_right;
 | |
|         if (rchild->avl_left == NULL)
 | |
|         {
 | |
|             *nodeplace = rchild;
 | |
|             rchild->avl_left = node->avl_left;
 | |
|             if (rchild->avl_left != NULL)
 | |
|                 rchild->avl_left->parent = rchild;
 | |
|             rchild->parent = node->parent;
 | |
|             util_avl_rebalance(rchild, root);
 | |
|             node = rchild->parent;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             struct util_avl_struct *successor = rchild->avl_left;
 | |
|             struct util_avl_struct *sparent = rchild;
 | |
|             while (successor->avl_left != NULL)
 | |
|             {
 | |
|                 sparent = successor;
 | |
|                 successor = successor->avl_left;
 | |
|             }
 | |
|             *nodeplace = successor;
 | |
|             sparent->avl_left = successor->avl_right;
 | |
|             successor->avl_left = node->avl_left;
 | |
|             successor->avl_right = node->avl_right;
 | |
| 
 | |
|             if (successor->avl_left != NULL)
 | |
|                 successor->avl_left->parent = successor;
 | |
|             successor->avl_right->parent = successor;
 | |
| 
 | |
|             if (sparent->avl_left != NULL)
 | |
|                 sparent->avl_left->parent = sparent;
 | |
|             successor->parent = node->parent;
 | |
|             util_avl_rebalance(sparent, root);
 | |
|             node = successor;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* rebalance */
 | |
|     util_avl_rebalance(node, root);
 | |
|     return;
 | |
| }
 |