前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >libuv源码阅读(5.1)--tree.h之伸展树

libuv源码阅读(5.1)--tree.h之伸展树

原创
作者头像
wanyicheng
修改2021-03-08 09:54:46
4350
修改2021-03-08 09:54:46
举报

伸展树:

可以参考我另外个博客,当时学习C的时候写的:

点这里 https://juejin.cn/post/6892567524118888462

代码语言:javascript
复制
// 根节点类型声明
#define SPLAY_HEAD(name, type)                                                \
struct name {                                                                 \
  struct type *sph_root; /* root of the tree */                               \
}

// 初始化 root 节点
#define SPLAY_INITIALIZER(root)                                               \
  { NULL }

// 初始化 root 节点
#define SPLAY_INIT(root) do {                                                 \
  (root)->sph_root = NULL;                                                    \
} while (/*CONSTCOND*/ 0)

// 每个伸展树节点都有左右2个子节点
#define SPLAY_ENTRY(type)                                                     \
struct {                                                                      \
  struct type *spe_left;          /* left element */                          \
  struct type *spe_right;         /* right element */                         \
}

// 取得对应的指针
#define SPLAY_LEFT(elm, field)    (elm)->field.spe_left
#define SPLAY_RIGHT(elm, field)   (elm)->field.spe_right
#define SPLAY_ROOT(head)          (head)->sph_root
#define SPLAY_EMPTY(head)         (SPLAY_ROOT(head) == NULL)

// 朝着右边旋转 也就是目前从 根节点往下的 3个节点为 / 这种一字型排列方式 
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {                             \
  SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);              \
  SPLAY_RIGHT(tmp, field) = (head)->sph_root;                                 \
  (head)->sph_root = tmp;                                                     \
} while (/*CONSTCOND*/ 0)

// 上面情况的对称情况 \ 这种排列
#define SPLAY_ROTATE_LEFT(head, tmp, field) do {                              \
  SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);              \
  SPLAY_LEFT(tmp, field) = (head)->sph_root;                                  \
  (head)->sph_root = tmp;                                                     \
} while (/*CONSTCOND*/ 0)

// 把新发现的 比目前元素大的节点 接入 right 节点的左节点下面 然后同时 移动根节点 和 right节点
#define SPLAY_LINKLEFT(head, tmp, field) do {                                 \
  SPLAY_LEFT(tmp, field) = (head)->sph_root;                                  \
  tmp = (head)->sph_root;                                                     \
  (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);                     \
} while (/*CONSTCOND*/ 0)

// 把新发现的 比目前元素小的节点 接入 left 节点的右节点下面 然后同时 移动根节点 和 left节点
#define SPLAY_LINKRIGHT(head, tmp, field) do {                                \
  SPLAY_RIGHT(tmp, field) = (head)->sph_root;                                 \
  tmp = (head)->sph_root;                                                     \
  (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);                    \
} while (/*CONSTCOND*/ 0)

// 找到最终节点后 交换 root 的左右节点 和 left指针的右节点 right指针的左节点
#define SPLAY_ASSEMBLE(head, node, left, right, field) do {                   \
  SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);             \
  SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);            \
  SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);             \
  SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);             \
} while (/*CONSTCOND*/ 0)

/* Generates prototypes and inline functions */

#define SPLAY_PROTOTYPE(name, type, field, cmp)                               \
void name##_SPLAY(struct name *, struct type *);                              \
void name##_SPLAY_MINMAX(struct name *, int);                                 \
struct type *name##_SPLAY_INSERT(struct name *, struct type *);               \
struct type *name##_SPLAY_REMOVE(struct name *, struct type *);               \

// 寻找目标元素 做一次伸展操作 把目标元素或者最接近目标元素的节点上升到根节点                                                                                                                                                            \/* Finds the node with the same key as elm */                                 \
static __inline struct type *                                                 \
name##_SPLAY_FIND(struct name *head, struct type *elm)                        \
{                                                                             \
  if (SPLAY_EMPTY(head))                                                      \
    return(NULL);                                                             \
  name##_SPLAY(head, elm);                                                    \
  if ((cmp)(elm, (head)->sph_root) == 0)                                      \
    return (head->sph_root);                                                  \
  return (NULL);                                                              \
}                                                                             \

// 寻找树中下一个比元素大的节点 做一次伸展 然后沿着新节点的右子树的左子树一直往下                                                                                                                                                                                                                                         \
static __inline struct type *                                                 \
name##_SPLAY_NEXT(struct name *head, struct type *elm)                        \
{                                                                             \
  name##_SPLAY(head, elm);                                                    \
  if (SPLAY_RIGHT(elm, field) != NULL) {                                      \
    elm = SPLAY_RIGHT(elm, field);                                            \
    while (SPLAY_LEFT(elm, field) != NULL) {                                  \
      elm = SPLAY_LEFT(elm, field);                                           \
    }                                                                         \
  } else                                                                      \
    elm = NULL;                                                               \
  return (elm);                                                               \
}                                                                             \
                                                                              \
static __inline struct type *                                                 \
name##_SPLAY_MIN_MAX(struct name *head, int val)                              \
{                                                                             \
  name##_SPLAY_MINMAX(head, val);                                             \
  return (SPLAY_ROOT(head));                                                  \
}
// 寻找最大或者最小值

/* Main splay operation.
 * Moves node close to the key of elm to top
 */
#define SPLAY_GENERATE(name, type, field, cmp)                                \
struct type *                                                                 \
name##_SPLAY_INSERT(struct name *head, struct type *elm)                      \
{                                                                             \
    if (SPLAY_EMPTY(head)) {                                                  \
      SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;                \
    } else {                                                                  \
      int __comp;                                                             \
      name##_SPLAY(head, elm);                                                \
      __comp = (cmp)(elm, (head)->sph_root);                                  \
      if(__comp < 0) {                                                        \
        SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);         \
        SPLAY_RIGHT(elm, field) = (head)->sph_root;                           \
        SPLAY_LEFT((head)->sph_root, field) = NULL;                           \
      } else if (__comp > 0) {                                                \
        SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);       \
        SPLAY_LEFT(elm, field) = (head)->sph_root;                            \
        SPLAY_RIGHT((head)->sph_root, field) = NULL;                          \
      } else                                                                  \
        return ((head)->sph_root);                                            \
    }                                                                         \
    (head)->sph_root = (elm);                                                 \
    return (NULL);                                                            \
}                                                                             \
// 插入元素 非空情况下  做一次伸展 然后根据目标元素和根节点的比较 决定如何插入

// 删除元素 伸展一次后 根据是否左右子树都存在 如果都存在 则 把原根节点的左子树取出来 做一次伸展 把最接近原节点值的元素作为新节点                                                                                                                                                                                                                                                                                                                        \
struct type *                                                                 \
name##_SPLAY_REMOVE(struct name *head, struct type *elm)                      \
{                                                                             \
  struct type *__tmp;                                                         \
  if (SPLAY_EMPTY(head))                                                      \
    return (NULL);                                                            \
  name##_SPLAY(head, elm);                                                    \
  if ((cmp)(elm, (head)->sph_root) == 0) {                                    \
    if (SPLAY_LEFT((head)->sph_root, field) == NULL) {                        \
      (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);                \
    } else {                                                                  \
      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
      (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);                 \
      name##_SPLAY(head, elm);                                                \
      SPLAY_RIGHT((head)->sph_root, field) = __tmp;                           \
    }                                                                         \
    return (elm);                                                             \
  }                                                                           \
  return (NULL);                                                              \
}                                                                             \
                                                                              \
void                                                                          \
name##_SPLAY(struct name *head, struct type *elm)                             \
{                                                                             \
  struct type __node, *__left, *__right, *__tmp;                              \
  int __comp;                                                                 \
                                                                              \
  SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;            \
  __left = __right = &__node;                                                 \
                                                                              \
  while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {                      \
    if (__comp < 0) {                                                         \
      __tmp = SPLAY_LEFT((head)->sph_root, field);                            \
      if (__tmp == NULL)                                                      \
        break;                                                                \
      if ((cmp)(elm, __tmp) < 0){                                             \
        SPLAY_ROTATE_RIGHT(head, __tmp, field);                               \
        if (SPLAY_LEFT((head)->sph_root, field) == NULL)                      \
          break;                                                              \
      }                                                                       \
      SPLAY_LINKLEFT(head, __right, field);                                   \
    } else if (__comp > 0) {                                                  \
      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
      if (__tmp == NULL)                                                      \
        break;                                                                \
      if ((cmp)(elm, __tmp) > 0){                                             \
        SPLAY_ROTATE_LEFT(head, __tmp, field);                                \
        if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                     \
          break;                                                              \
      }                                                                       \
      SPLAY_LINKRIGHT(head, __left, field);                                   \
    }                                                                         \
  }                                                                           \
  SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                      \
}                                                                             \
                                                                              \
/* Splay with either the minimum or the maximum element                       \
 * Used to find minimum or maximum element in tree.                           \
 */                                                                           \
void name##_SPLAY_MINMAX(struct name *head, int __comp)                       \
{                                                                             \
  struct type __node, *__left, *__right, *__tmp;                              \
                                                                              \
  SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;            \
  __left = __right = &__node;                                                 \
                                                                              \
  while (1) {                                                                 \
    if (__comp < 0) {                                                         \
      __tmp = SPLAY_LEFT((head)->sph_root, field);                            \
      if (__tmp == NULL)                                                      \
        break;                                                                \
      if (__comp < 0){                                                        \
        SPLAY_ROTATE_RIGHT(head, __tmp, field);                               \
        if (SPLAY_LEFT((head)->sph_root, field) == NULL)                      \
          break;                                                              \
      }                                                                       \
      SPLAY_LINKLEFT(head, __right, field);                                   \
    } else if (__comp > 0) {                                                  \
      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
      if (__tmp == NULL)                                                      \
        break;                                                                \
      if (__comp > 0) {                                                       \
        SPLAY_ROTATE_LEFT(head, __tmp, field);                                \
        if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                     \
          break;                                                              \
      }                                                                       \
      SPLAY_LINKRIGHT(head, __left, field);                                   \
    }                                                                         \
  }                                                                           \
  SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                      \
}

// 根据 参数找 极值 正数就往右侧不断找 负数就是往左侧不断寻找

#define SPLAY_NEGINF  -1
#define SPLAY_INF     1

#define SPLAY_INSERT(name, x, y)  name##_SPLAY_INSERT(x, y)
#define SPLAY_REMOVE(name, x, y)  name##_SPLAY_REMOVE(x, y)
#define SPLAY_FIND(name, x, y)    name##_SPLAY_FIND(x, y)
#define SPLAY_NEXT(name, x, y)    name##_SPLAY_NEXT(x, y)
#define SPLAY_MIN(name, x)        (SPLAY_EMPTY(x) ? NULL                      \
                                  : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
#define SPLAY_MAX(name, x)        (SPLAY_EMPTY(x) ? NULL                      \
                                  : name##_SPLAY_MIN_MAX(x, SPLAY_INF))

#define SPLAY_FOREACH(x, name, head)                                          \
  for ((x) = SPLAY_MIN(name, head);                                           \
       (x) != NULL;                                                           \
       (x) = SPLAY_NEXT(name, head, x))

最主要的方法就是这个伸展函数:

先仔细说明下面几个变量的意思:

在遍历过程中,head->sph_root 始终指向目前正在处理的根节点;__left 左侧最大树,它的右节点不断延伸接入新发现的节点,这棵树的所有节点都比当前根节点小;__right 右侧最小树,它的左节点不断延伸接入新发现的节点,这棵树所有的节点都比根节点大; __node作为临时根节点,它的右节点指针指向 __left 指针第一次被赋值的时候的节点 也就是 左侧最大树的根节点 ,它的左节点指针指向 右侧最小树.

最后一行就是调用前面的方法,交互 4个指针 得到最终的伸展树。它符合二叉树的大小顺序,同时根节点就是目标节点 或者 最接近的目标节点。

代码语言:javascript
复制
void                                                                          \
name##_SPLAY(struct name *head, struct type *elm)                             \
{                                                                             \
  struct type __node, *__left, *__right, *__tmp;                              \
  int __comp;                                                                 \
                                                                              \
  SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;            \
  __left = __right = &__node;                                                 \
                                                                              \
  while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {                      \
    if (__comp < 0) {                                                         \
      __tmp = SPLAY_LEFT((head)->sph_root, field);                            \
      if (__tmp == NULL)                                                      \
        break;                                                                \
      if ((cmp)(elm, __tmp) < 0){                                             \
        SPLAY_ROTATE_RIGHT(head, __tmp, field);                               \
        if (SPLAY_LEFT((head)->sph_root, field) == NULL)                      \
          break;                                                              \
      }                                                                       \
      SPLAY_LINKLEFT(head, __right, field);                                   \
    } else if (__comp > 0) {                                                  \
      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
      if (__tmp == NULL)                                                      \
        break;                                                                \
      if ((cmp)(elm, __tmp) > 0){                                             \
        SPLAY_ROTATE_LEFT(head, __tmp, field);                                \
        if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                     \
          break;                                                              \
      }                                                                       \
      SPLAY_LINKRIGHT(head, __left, field);                                   \
    }                                                                         \
  }                                                                           \
  SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                      \
}

总结:虽然伸展树在 libuv 没有用到,但是可以复习一下 伸展树的实现,splay那个伸展函数还是很微妙的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com