TAILQ 是链表结构

几种常用的队列函数:

TAILQ_HEAD()	                    //定义队列头
TAILQ_ENTRY	()                   //队列实体定义
TAILQ_INIT()                  // 初始化队列
TAILQ_FOREACH()	              // 对队列进行遍历操作
TAILQ_INSERT_BEFORE()	          // 在指定元素之前插入元素
TAILQ_INSERT_TAIL()	          // 在队列尾部插入元素
TAILQ_EMPTY()	                  // 检查队列是否为空
TAILQ_REMOVE()	                //从队列中移除元素

(1)为了实现一个链表队列,首先我们得定义一个结构体作为链表队列结点,结构体定义如下:

1 typedef struct Node {
2    /*数据类型*/
3    int id;
4   
5    /*link:链接器*/
6    TAILQ_ENTRY(Node) link;
7  } Node;

上面结构体定义中我们使用了 TAILQ_ENTRY 来定义结点间的链接,TAILQ_ENTRY宏定义如下:

1 #define TAILQ_ENTRY(type)                                              
2 struct {                                                                
3       type *tqe_next;       /* next element */                        
4        type **tqe_prev;      /* address of previous next  element */    
 }

将TAILQ_ENTRY宏定义在我们定义的Node结构体中展开如下:

1  typedef struct Node {
2     /*some attr*/
3    int id;
4
5 /*link*/
6        struct { 
7            Node *tqe_next; 
8          Node **tqe_prev; 
9         } link;
10    } Node;

在link属性中包含了两个元素,tqe_next和tqe_prev,其中tqe_next指向下一个Node结点,,tqe_pre是指向前一个结点的link.tqe_next属性,所以它才会是一个二级指针的形式。直接上图,假设我们已经使用上述结构体创建了一个队列,该队列的指针指向如下图所示:

在这里插入图片描述
(2)创建好链表队列结点结构体后,我们需要创建一个链表队列头方便我们对队列进行操作,创建代码如下:

1 TAILQ_HEAD(head, Node) head;

创建链表头使用的宏定义如下:

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

宏定义展开后代码如下:

1 struct head { 
2    Node * tqh_first; 
3    Node ** tqh_last; 
4         } head;

该头也有两属性,第一个tqh_first指向队列的第一个元素,第二个属性也是一个二级指针,虽然命名为last,但是不是指向链表队列最后一个元素,而是指向链表队列最后一个元素的link.next属性,如下图所示:
在这里插入图片描述
(3)准备好队列头的结点结构体后,可以开始初始化一个链表队列的,初始化链表队列代码如下:

1 TAILQ_INIT(head);

队列初始化宏定义如下:

#define TAILQ_INIT(head) do {                                          \
        (head)->tqh_first = NULL;                                       \
        (head)->tqh_last = &(head)->tqh_first;                          \
} while (/*CONSTCOND*/0)

以上代码比较简单,就是对队列头的两个属性赋初始值
(4)在链表队列中插入元素,既可以在链表队列的头部插入元素,也可以在链表队列的尾部插入元素,还可以在链表队列的中间插入元素(变异的队列。。。),接下来对插入元素的几种方式分别介绍。
(1)在链表队列尾部插入元素代码如下,动态分配一个Node结点node01,并将其插入到队列尾部

Node * node01 = (Node *)malloc(sizeof(Node));
TAILQ_INSERT_TAIL(head, node01, link);
所使用的TAILQ_INSERT_TAIL定义如下:

```c
#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 (/*CONSTCOND*/0)

将在链表队列尾部插入元素的代码展开如下:

```c
do { 
    (node01)->link.tqe_next = ((void *)0); 
	(node01)->link.tqe_prev = (head)->tqh_last; 
	*(head)->tqh_last = (node01); 
	(head)->tqh_last = &(node01)->link.tqe_next; 
} while ( 0)

插入元素宏定义展开后总共4行代码,下图图示了这4行代码实现流程:
假设队列中已经有3个结点,我们需要在尾部插入第4个结点
在这里插入图片描述
第1行代码将第四个结点的tqe_next设置为(void *)0,可以直接理解为置为NULL。
第2行代码中将tqe_pre指向前一个结点的tqe_next属性,因为head中的tqh_last指向了链表最后一个元素的tqe_next属性,所以这一步只需要将head->tqh_last的值直接赋给tqe_pre即可。
在这里插入图片描述
第3行代码实现将原队列最后一个元素tqe_next指向新增的node结点。
在这里插入图片描述
第4行代码就是将tqh_last指向新增结点的tqe_next属性。
在这里插入图片描述
通过上述4行代码我们便完成了在队列尾部新增结点的操作。通过上述操作我们可以发现,在链表尾部新增结点只需要4步,并不会因为链表节点数变多而导致时间复杂度增大,这就是head节点的意义,通过一个二级指针tql_last指向最后一个元素的tqe_next, 使得在队列尾部插入元素的时间复杂度为O(1)。
遍历节点

遍历节点
使用QLIST_FOREACH或者QLIST_FOREACH_SAFE,QLIST_FOREACH_SAFE是为了防止遍历过程中删除了节点,从而导致le_next被释放掉,中断了遍历。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值