链表之STAILQ

Posted 车子 chezi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表之STAILQ相关的知识,希望对你有一定的参考价值。

文章目录

STAILQ 示意图

STAILQ 和 LIST 的不同是,head 里面有个指针 stqh_last,指向最后一个节点的 stqe_next

注意:有的地方也把 STAILQ 叫 simple queue,将接口前缀改为 SIMPLEQ_

接口和实现

先贴代码。(注意:不同版本的代码可能不同)

/*
 * Singly-linked Tail queue declarations.
 */
#define	STAILQ_HEAD(name, type)						\\
struct name 								\\
	struct type *stqh_first;/* first element */			\\
	struct type **stqh_last;/* addr of last next element */		\\


#define	STAILQ_HEAD_INITIALIZER(head)					\\
	 NULL, &(head).stqh_first 

#define	STAILQ_ENTRY(type)						\\
struct 								\\
	struct type *stqe_next;	/* next element */			\\


/*
 * Singly-linked Tail queue functions.
 */
#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)

#define	STAILQ_FIRST(head)	((head)->stqh_first)

#define	STAILQ_FOREACH(var, head, field)				\\
	for((var) = STAILQ_FIRST((head));				\\
	   (var);							\\
	   (var) = STAILQ_NEXT((var), field))

#define	STAILQ_INIT(head) do 						\\
	STAILQ_FIRST((head)) = NULL;					\\
	(head)->stqh_last = &STAILQ_FIRST((head));			\\
 while (0)

#define	STAILQ_INSERT_AFTER(head, tqelm, elm, field) do 		\\
	if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\\
		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\\
	STAILQ_NEXT((tqelm), field) = (elm);				\\
 while (0)

#define	STAILQ_INSERT_HEAD(head, elm, field) do 			\\
	if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL)	\\
		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\\
	STAILQ_FIRST((head)) = (elm);					\\
 while (0)

#define	STAILQ_INSERT_TAIL(head, elm, field) do 			\\
	STAILQ_NEXT((elm), field) = NULL;				\\
	*(head)->stqh_last = (elm);					\\
	(head)->stqh_last = &STAILQ_NEXT((elm), field);			\\
 while (0)

#define	STAILQ_LAST(head, type, field)					\\
	(STAILQ_EMPTY(head) ?						\\
		NULL :							\\
	        ((struct type *)					\\
		((char *)((head)->stqh_last) - offsetof(struct type, field))))

#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)

#define	STAILQ_REMOVE(head, elm, type, field) do 			\\
	if (STAILQ_FIRST((head)) == (elm)) 				\\
		STAILQ_REMOVE_HEAD(head, field);			\\
									\\
	else 								\\
		struct type *curelm = STAILQ_FIRST((head));		\\
		while (STAILQ_NEXT(curelm, field) != (elm))		\\
			curelm = STAILQ_NEXT(curelm, field);		\\
		if ((STAILQ_NEXT(curelm, field) =			\\
		     STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\\
			(head)->stqh_last = &STAILQ_NEXT((curelm), field);\\
									\\
 while (0)

#define	STAILQ_REMOVE_HEAD(head, field) do 				\\
	if ((STAILQ_FIRST((head)) =					\\
	     STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)		\\
		(head)->stqh_last = &STAILQ_FIRST((head));		\\
 while (0)

#define	STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do 			\\
	if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL)	\\
		(head)->stqh_last = &STAILQ_FIRST((head));		\\
 while (0)

#define STAILQ_REMOVE_AFTER(head, elm, field) do 			\\
	if ((STAILQ_NEXT(elm, field) =					\\
	     STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)	\\
		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\\
 while (0)

举例

咱们看个例子,例子来自:https://manpages.courier-mta.org/htmlman3/stailq.3.html

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/queue.h>

struct entry 
    int data;
    STAILQ_ENTRY(entry) entries;        /* Singly linked tail queue */
;

STAILQ_HEAD(stailhead, entry);

int
main(void)

    struct entry *n1, *n2, *n3, *np;
    struct stailhead head;                  /* Singly linked tail queue
                                               head */

    STAILQ_INIT(&head);                     /* Initialize the queue */

    n1 = malloc(sizeof(struct entry));      /* Insert at the head */
    STAILQ_INSERT_HEAD(&head, n1, entries);

    n1 = malloc(sizeof(struct entry));      /* Insert at the tail */
    STAILQ_INSERT_TAIL(&head, n1, entries);

    n2 = malloc(sizeof(struct entry));      /* Insert after */
    STAILQ_INSERT_AFTER(&head, n1, n2, entries);

    STAILQ_REMOVE(&head, n2, entry, entries); /* Deletion */
    free(n2);

    n3 = STAILQ_FIRST(&head);
    STAILQ_REMOVE_HEAD(&head, entries);     /* Deletion from the head */
    free(n3);

    n1 = STAILQ_FIRST(&head);
    n1->data = 0;
    for (int i = 1; i < 5; i++) 
        n1 = malloc(sizeof(struct entry));
        STAILQ_INSERT_HEAD(&head, n1, entries);
        n1->data = i;
    
                                            /* Forward traversal */
    STAILQ_FOREACH(np, &head, entries)
        printf("%i\\n", np->data);
                                            /* TailQ deletion */
    n1 = STAILQ_FIRST(&head);
    while (n1 != NULL) 
        n2 = STAILQ_NEXT(n1, entries);
        free(n1);
        n1 = n2;
    
    STAILQ_INIT(&head);

    exit(EXIT_SUCCESS);

代码分析

STAILQ_ENTRY 和 STAILQ_HEAD

struct entry 
    int data;
    STAILQ_ENTRY(entry) entries;        /* Singly linked tail queue */
;

STAILQ_HEAD(stailhead, entry);

宏替换后是

struct entry 
    int data;
    struct  
        struct entry *stqe_next; 
     entries;
;

struct stailhead  
    struct entry *stqh_first; 
    struct entry **stqh_last; 
;

其实和 SLIST 差不多,唯一的不同是第 10 行,多了一个指向尾节点的指针(准确地说是指向尾节点的 stqe_next)

STAILQ_INIT 和 STAILQ_INSERT_HEAD

    struct entry *n1, *n2, *n3, *np;
    struct stailhead head;                  /* Singly linked tail queue
                                               head */

    STAILQ_INIT(&head);                     /* Initialize the queue */

    n1 = malloc(sizeof(struct entry));      /* Insert at the head */
    STAILQ_INSERT_HEAD(&head, n1, entries);

5:宏展开是

(&head)->stqh_first = ((void *)0); 
(&head)->stqh_last = &(&head)->stqh_first; 

链表的初始化为空,stqh_first 等于 NULL,stqh_last 指向自己的 stqh_first

8:宏展开是

if (((n1)->entries.stqe_next = (&head)->stqh_first) == ((void *)0)) 
	(&head)->stqh_last = &(n1)->entries.stqe_next; 
(&head)->stqh_first = (n1); 

把 n1 插入链表的第一个位置,会影响哪个指针的值呢?

  1. stqh_first
  2. n1 的 stqe_next
  3. 如果链表为空,那么插入 n1 后,stqh_last 也要改变

第 1 行解决了 2

第 2 行解决了 3

第 3 行解决了 1

STAILQ_INSERT_TAIL

    n1 = malloc(sizeof(struct entry));      /* Insert at the tail */
    STAILQ_INSERT_TAIL(&head, n1, entries);

2:宏替换是

    (n1)->entries.stqe_next = ((void *)0); 
    *(&head)->stqh_last = (n1); 
    (&head)->stqh_last = &(n1)->entries.stqe_next; 

把 n1 插入链表的最后一个位置,会影响哪些指针的值呢?

  1. stqh_last
  2. n1 的 stqe_next,需要为 NULL
  3. 原先的最后一个节点(假设是 n8)的 stqe_next

第 1 行解决了 2

第 2 行有点麻烦,(&head)->stqh_last 指向 n8 节点的 stqe_next,*(&head)->stqh_last 就是 n8 节点的 stqe_next,赋值 n1 给它,解决了 3

第 3 行解决了 1

STAILQ_INSERT_AFTER

    n2 = malloc(sizeof(struct entry));      /* Insert after */
    STAILQ_INSERT_AFTER(&head, n1, n2, entries);

第 2 行的意思是把 n2 插入到 n1 的后面

代码展开后是

if (((n2)->entries.stqe_next = (n1)->entries.stqe_next) == ((void *)0)) 
    (&head)->stqh_last = &(n2)->entries.stqe_next; 
(n1)->entries.stqe_next = (n2);

把 n2 插入到 n1 的后面,会影响哪些指针的值呢?

  1. n1 的 stqe_next
  2. n2 的 stqe_next
  3. 如果 n2 是最后一个节点,还要修改 (&head)->stqh_last

第 1 行的 (n2)->entries.stqe_next = (n1)->entries.stqe_next) 解决了 2

第 3 行解决了 1

第 2 行解决了 3

STAILQ_REMOVE

    STAILQ_REMOVE(&head, n2, entry, entries); /* Deletion */
    free(n2);

第 1 行展开是,有点长

if ((&head)->stqh_first == (n2))  
    if ((((&head))->stqh_first = ((&head))->stqh_first->entries.stqe_next) == ((void *)0)) 
        ((&head))->stqh_last = &((&head))->stqh_first; 
 else  
    struct entry *curelm = (&head)->stqh_first; 
    while (curelm->entries.stqe_next != (n2)) 
        curelm = curelm->entries.stqe_next; 
    if ((curelm->entries.stqe_next = curelm->entries.stqe_next->entries.stqe_next) == ((void *)0)) 
        (&head)->stqh_last = &(curelm)->entries.stqe_next; 
 

分 2 种情况。n2 是不是第一个节点(第 1 行代码),如果是,需要修改的指针有

  1. (&head)->stqh_first
  2. 如果 n2 是仅有的一个节点,那么还需要修改 (&head))->stqh_last

第 2 行的前半句解决了 1

第 3 行解决了 2

如果 n2 不是第一个节点,那就执行 5-10,先查找 n2 (6-7 行),当找到的时候, curelm 代表 n2 前面的那个节点。此时需要修改的指针有:

  1. curelm 的 stqe_next
  2. 如果 n2 是最后一个节点,删除它后需要修改 (&head))->stqh_last

第 8 行的前半句解决了 1

第 9 行解决了 2

STAILQ_FIRST 和 STAILQ_REMOVE_HEAD

    n3 = STAILQ_FIRST(&head);
    STAILQ_REMOVE_HEAD(&head, entries);     /* Deletion from the head */
    free(n3);

STAILQ_FIRST 比较简单,就是取链表的第一个节点,注意,不是删除

第 1 行展开:n3 = ((&head)->stqh_first);

STAILQ_REMOVE_HEAD 是删除第一个节点

第 2 行展开:

if (((&head)->stqh_first = (&head)->stqh_first->entries.stqe_next) == ((void *)0)) 
    (&head)链表之STAILQ

第二百八十节,MySQL数据库-外键链表之一对多

数据结构与算法-线性表之静态链表

数据结构学习笔记二线性表之链表篇(双向链表)

数据结构线性表之实现单循环链表

数据结构(线性表之单链表)