TAIL Queue数据结构

来源:互联网 发布:ubuntu jdk安装 编辑:程序博客网 时间:2024/06/03 15:15

        看源代码的时候发现一个新的数据结构,然后我就好好的画了一下这个数据结构的图形,一是为了记录一下我自己的分析过程,二是和大家分享一下这个数据结构。这个数据结构的名字叫TAIL QUEUE感觉很高大上。所以我想分析一下这个数据结构,这个数据结构里面使用到了很多的宏定义。

这个结构体的定义如下:

typedef struct ConfNode_ {    char *name;    char *val;    int is_seq;    int allow_override;    struct ConfNode_ *parent;    TAILQ_HEAD(, ConfNode_) head;    TAILQ_ENTRY(ConfNode_) next;} ConfNode;
ConfNode结构的图形是这个样子:


TAIL_HEAD又是什么呢?这是一个宏定义,结构如下:

#define TAILQ_HEAD(name, type)\struct name {\struct type *tqh_first;/* first element */\struct type **tqh_last;/* addr of last next element */\}

里面包含了一个两个成员,一个是指向type类型的tqh_first指针,一个是tqh_last,这个是指向指针的指针。这个type就是传过来的ConfNode_,说明这个类型和之前的声明的ConfNode的类型是一样的。里面的注释也写的很清楚,tqh_first指向第一个元素,tqh_last是指向next element的地址。

TAIL_ENTRY这个宏定义如下:

#define TAILQ_ENTRY(type)\struct {\struct type *tqe_next;/* next element */\struct type **tqe_prev;/* address of previous next element */\}
同样,这个的解释和上面那个是一样的,没有不同,解释是tqe_next指向的是下一个结点,tqe_prev指向的是上一个元素的地址。

通过替换现在ConfNode结点的代码就如下:

typedef struct ConfNode_ {    char *name;    char *val;    int is_seq;    int allow_override;    struct ConfNode_ *parent;    struct name {struct type *tqh_first;/* first element */struct type **tqh_last;/* addr of last next element */     } head;    struct {struct type *tqe_next;/* next element */struct type **tqe_prev;/* address of previous next element */      } next;} ConfNode;
现在的结构图变成这个样子了:



       接下来就是申明结点的操作:

ConfNode *ConfNodeNew(void){    ConfNode *new;    new = SCCalloc(1, sizeof(*new));    if (unlikely(new == NULL)) {        return NULL;    }    /* By default we allow an override. */    new->allow_override = 1;    TAILQ_INIT(&new->head);    return new;}

    这里面主要是TAILQ_INIT这个宏的定义解释:

#defineTAILQ_INIT(head) do {\(head)->tqh_first = NULL;\(head)->tqh_last = &(head)->tqh_first;\} while (0)

这里主要是将刚分配的ConfNode结点元素里面的head变量里的tqh_first和tqh_last进行初始化。


这里的tqh_first初始化为NULL,tqh_last初始化为指向tqh_first,也就是说tqh_last里面出入的是tqh_first的地址。

     接下来就是准备插入元素的过程了,插入元素使用的是TAILQ_INSERT_TAIL这个宏定义,在这之前肯定是先声明一个root结点的。

<pre name="code" class="cpp">ConfNode *parent = NULL;
ConfNode *node = parent;

     刚开始插入的时候,由于这两个结点都是NULL,还没有分配任何空间,所以肯定是不行的,在里面有地方是给它分配空间的,这个代码就是这里:

node = ConfNodeNew();
    因为node和parent指向的是同一个地方,所以开始插入的时候比较特殊。

TAILQ_INSERT_TAIL(&parent->head, node, next);

TAILQ_INSERT_TAIL宏定义的实现如下:

#define TAILQ_INSERT_TAIL(head, elm, field) do {\(elm)->field.tqe_next = NULL;\(elm)->field.tqe_prev = (head)->tqh_last;\*(head)->tqh_last = (elm);\(head)->tqh_last = &(elm)->field.tqe_next;\} while (0)
      呀呀呀,这个东西看上去有点绕了,那我就用画图的形式来解释吧。其实它做的工作也就那么点并没有很复杂。刚开始分配空间的时候tqh_first和tqh_last是初始化过的,还记得吧,tqh_last指向tqh_first。

第一步刚开始初始化的情况如下:


接下来执行第一步:

(elm)->field.tqe_next = NULL;


接下来就是指向下面的代码:

(elm)->field.tqe_prev = (head)->tqh_last;
      第一次插入的时候注意的是elm和head其实是同一个结点,所以都是对这个结点进行操作,这个执行的意思是tqe_prev和tqh_last是指向同一个地方的,tqh_last我们知道,指向的是tqh_first。所以tqe_prev也是指向tqh_first。


接下来的两行代码执行的其实就是修改了里面的指针:

*(head)->tqh_last = (elm);(head)->tqh_last = &(elm)->field.tqe_next;
第一行代码我们知道tqh_last指向的是tqh_first的地址,所以操作*(head)->tqh_last其实就是操作的tqh_first。而这个tqh_first = NULL,所以第一行代码就是修改tqh_first的值,使得tqh_first指向这个结构体的开始。


最后一步就是修改tqh_last的值了,使这个值指向之后一个结点,但这个时候里面的结点只有一个,所以tqh_last指向的是tqe_next的地址。


    最后我将画一下多个结点的情况,这个也就是在里面修改一下指针而已。图示如下:


       如果看不清楚请原谅,我已经很注意了。如果有什么问题请大家提出来,希望大家讲讲这个数据机构的优点,我是有点没有想明白,双向循环链表也是可以实现的啊。





0 0
原创粉丝点击