C数据结构:链表高级篇

单链表的定义

      由于顺序表的插入删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储——单链表。单链表通过一组任意的存储单元来存储线性表中的数据元素,不需要使用地址连续的存储单元,因此它不要求在逻辑上相邻的两个元素在物理位置上也相邻。

  单链表的特点:单链表不要求逻辑上相邻的两个元素在物理位置上也相邻,因此不需要连续的存储空间。
单链表是非随机的存储结构,即不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。
 对于每个链表结点,除了存放元素自身的信息外,还需要存放一个指向其后继的指针。
 单链表中结点类型的描述:

        有头节点:指针指向的一个数据是头节点,头节点指向真正的数据节点。
        无头节点:指针直接指向数据节点。
        区别:有头节点操作可以直接对头节点进行操作,便于管理,无头节点在头节点插入元素需要修改指针指向的元素。

头结点和头指针的区分:不管带不带头结点,头指针始终指向单链表的第一个结点,而头结点是带头结点的单链表中的第一个结点,结点内通常不存储信息。
 那么单链表的初始化操作就是申请一个头结点,将指针域置空。

<span style="color:#000000"><span style="background-color:#282c34">


</span></span>

有头节点的单链表

 通常会用头指针来标识一个单链表,头指针为NULL时表示一个空表。但是,为了操作方便,会在单链表的第一个结点之前附加一个结点,称为头结点。头结点的数据域可以不设任何信息,也可以记录表长等信息。头结点的指针域指向线性表的第一个元素结点。在本链表中,指定了头节点不是下标为0 的节点,0节点是头节点后的一个节点。

typedef int datatype;

typedef struct note_st
{
    datatype data; //存放数据
    struct note_st* next; //存放下一个位置的指针
}list;

创建有头单链表


list* list_create()
{
    list* me; 
   me = malloc(sizeof(*me));
    if(me == NULL)
        return NULL;
        
    me->next = NULL; //头元素指向空

    return me; 
}

按照位置插入元素

这里所说的插入是将值为data的新结点插入到单链表L的第i个位置上。(不包括头结点)

 算法思想:从表头开始遍历,查找第 i-1个结点,即插入位置的前驱结点为p,然后令新结点newnode的指针域指向node的后继结点,再令结点node的指针域指向新结点*newnode。

int list_insert_at(list*me,int i,datatype*data)
{
    int j = 0;
    list* node = me,*newnode;

    if(i<0)
        return -1; 
    while(j< i && node != NULL)
    {   
        node = node->next;
        j++;

    }   
    if(node)
    {   
        newnode = malloc(sizeof(*newnode));
        if(newnode == NULL)
            return -2;
        newnode->data = *data;
        newnode->next = node->next;
        node->next = newnode;
        return 0;
    }
    else
    {
        return -3;
    }

}

显示元素

算法思想:声明一个指针p,从头结点指向的第一个结点开始,如果p不为空,那么就输出当前结点的值,并将p指向下一个结点,直到遍历到最后一个结点为止。

oid list_display(list*me)
{
    list*node = me->next;
    if(list_isempty(me) == 0)
        return;
    while(node != NULL)
    {
        printf("%d ",node->data );
        node = node->next;

    }
    printf("\n");

}


销毁链表

先获取到头节点执行的节点,不为空则删除全部信息,删除完毕后,最后删除头节目信息。


void list_destroy(list*me)
{
    list*next;
    list*node = me->next;

    while(node != NULL)
    {
        next  = node->next;
        free(node);
        node  = next;
    }
    free(me);
    return;
}

按位置插入排序


int list_order_insert(list* me,datatype *data)
{
    //按照顺序进行排序
    list *p = me,*q;
    while(p->next && (p->next->data < *data) )
    {
        p = p->next;
    }
    q = malloc(sizeof(*q));
    if(q == NULL)
        return -1;
    q->data = *data;
    q->next = p->next;
    p->next = q;
    return 0;


}

有头链表的全部实现

main.c

#include <stdlib.h>
#include <stdio.h>
#include "list.h"
int main()
{

    list*l;
    datatype arr[] = {11,2,33,4,55};
    l = list_create();
    if(l == NULL)
        exit(1);

    for(int i=0;i<sizeof(arr)/sizeof(*arr);i++ )
    {
        //if(list_insert_at(l,0,&arr[i]) != 0)
        if(list_order_insert(l,&arr[i]) != 0)
            exit(1);
    }

    list_insert_at(l,1,&arr[0]);

    list_display(l);
    int val = 11;
    list_delete(l,&val);

    list_display(l);

    list_destroy(l);

    return 0;
}

list.c

#include "list.h"
#include <stdlib.h>
#include <stdio.h>


list* list_create()
{
    list* me;
   me = malloc(sizeof(*me));
    if(me == NULL)
        return NULL;
    
    me->next = NULL;

    return me;
}

int list_insert_at(list*me,int i,datatype*data)
{
    int j = 0;
    list* node = me,*newnode;

    if(i<0)
        return -1;
    while(j< i && node != NULL)
    {
        node = node->next;
        j++;

    }
    if(node)
    {
        newnode = malloc(sizeof(*newnode));
        if(newnode == NULL)
            return -2;
        newnode->data = *data;
        newnode->next = node->next;
        node->next = newnode;
        return 0;
    }
    else
    {
        return -3;
    }

}

void list_display(list*me)
{
    list*node = me->next;
    if(list_isempty(me) == 0)
        return;
    while(node != NULL)
    {
        printf("%d ",node->data );
        node = node->next;

    }
    printf("\n");

}


int list_order_insert(list* me,datatype *data)
{
    //按照顺序进行排序
    list *p = me,*q;
    while(p->next && (p->next->data < *data) )
    {
        p = p->next;
    }
    q = malloc(sizeof(*q));
    if(q == NULL)
        return -1;
    q->data = *data;
    q->next = p->next;
    p->next = q;
    return 0;


}

int list_delete_at(list* me,int i,datatype * data)
{
    int j = 0;
    list *p = me,*q;
    
    *data = -1;

    if(i< 0)
        return -1;

    while(j< i && p )
    {
        p = p->next;
        j++;
    }
    if(p)
    {
        q = p->next;
        p->next = q->next;
        *data = q->data;
        free(q);
        q = NULL;
        return 0;
    }
    else
    {
        return -2;
    }

}

int list_delete(list* me,datatype* data)
{
    list*p =me,*q;
    while(p->next && p->next->data != *data)
    {
        p = p->next;
    }
    if(p->next == NULL)
        return -1;
    
    q = p->next;
    p->next = q->next;
    free(q);
    q = NULL;
    return 0;
}


int list_isempty(list*me)
{
    if(me->next == NULL)
        return 0;
    return 1;
}

void list_destroy(list*me)
{
    list*next;
    list*node = me->next;

    while(node != NULL)
    {
        next  = node->next;
        free(node);
        node  = next;
    }
    free(me);
    return;
}



list.h

#ifndef LIST_H
#define LIST_H

typedef int datatype;

typedef struct note_st
{
    datatype data;
    struct note_st* next;
}list;


list* list_create();

int list_insert_at(list*,int i,datatype*);

int list_order_insert(list*,datatype *);

int list_delete_at(list*,int i,datatype *);

int list_delete(list*,datatype* );

int list_isempty(list*);

void list_display(list*me);

void list_destroy(list*);


#endif

makefile

CC = gcc
OBJS = main.o list.o
CFLAGS =  -g -Wall -c

all:$(OBJS)
	$(CC) $^  -o $@

%.o:%.c
	$(CC) $^ $(CFLAGS) -o $@	

clean:
	$(RM) -r all $(OBJS)

无头节点的单链表

结构体

#define NAMESIZE 32

struct score_st
{
    int id; 
    char name[NAMESIZE];
    int math;
    int chinese;
};

struct node_st
{
    struct score_st data;
    struct node_st* next;
};

插入元素

struct node_st* list_insert(struct node_st*list ,struct score_st*data)
{
    struct node_st* new;

    new =  malloc(sizeof(*new));
    if(new == NULL)
        return NULL;
    new->data = *data;
    new->next = NULL;

    new->next =list;
    list = new;
    return list; //要将list返回,否则会失去元素,这里的指针是值传递传递的,也可以使用二级指针

}

显示元素

void list_show(struct node_st*list)
{
    struct node_st*cur;
    for(cur = list;cur !=NULL;cur = cur->next)
    {   
        printf("id = %d name - %s math = %d chinese = %d \n",cur->data.id,cur->data.name,cur->data.math,cur->data.chinese);
    }   
}

查找元素


int list_find(struct node_st*list,int id) 
{
    struct node_st* cur;
    for(cur = list;cur !=NULL;cur = cur->next)
    {   
        if(cur->data.id == id) 
        {   
            printf("%d 已经找到了\n",cur->data.id);
            return 0;
        }   
    }   
    return -1; 
}

无头链表的全部实现

main.c

#include <stdlib.h>
#include <stdio.h>
#include "nohand.h"


int main()
{
    struct node_st* list = NULL;
    struct score_st temp;

    for(int i=0;i<7;i++)
    {
        temp.id = i;
        snprintf(temp.name,NAMESIZE,"stu%d",i);
        temp.math = rand()%100;
        temp.chinese = rand()%100;
        //list = list_insert(list,&temp);
        list_insert_point(&list,&temp);

    }

    list_show(list);

    list_delete(&list);
    printf("-------------------------------------------\n");
    // list_show(list);
    int i = 3;
    struct score_st*ptr;
    ptr = list_find(list,i);
    if(ptr == NULL)
    {
        printf("no find \n");
    }
    else
    {
        printf("find id = %d \n",ptr->id );
    }
    list_destroy(list);

    return 0;
}

nohead.h

#ifndef NOHAND_H__
#define NOHEAD_H__

#define NAMESIZE 32

struct score_st
{
    int id;
    char name[NAMESIZE];
    int math;
    int chinese;
};

struct node_st
{
    struct score_st data;
    struct node_st* next;
};

struct node_st* list_insert(struct node_st* ,struct score_st* );
int  list_insert_point(struct node_st** ,struct score_st* );


void list_show(struct node_st*);

int list_delete(struct node_st**);

struct score_st* list_find(struct node_st*,int);

void list_destroy(struct node_st*);

#endif

nohead.c

#include <stdlib.h>
#include <stdio.h>
#include "nohand.h"

struct node_st* list_insert(struct node_st*list ,struct score_st*data)
{
    struct node_st* new;

    new =  malloc(sizeof(*new));
    if(new == NULL)
        return NULL;
    new->data = *data;
    new->next = NULL;

    new->next =list;
    list = new;
    return list; //要将list返回,否则会失去元素

}
int list_insert_point(struct node_st**list ,struct score_st*data)
{
    struct node_st* new;
    new =  malloc(sizeof(*new));
    if(new == NULL)
        return -1;
    new->data = *data;
    new->next =*list;
    *list = new;
    return 0;
}


void list_show(struct node_st*list)
{
    struct node_st*cur;
    for(cur = list;cur !=NULL;cur = cur->next)
    {
        printf("id = %d name - %s math = %d chinese = %d \n",cur->data.id,cur->data.name,cur->data.math,cur->data.chinese);
    }
}
int list_delete(struct node_st** list)
{
    if(*list == NULL)
        return -1;
    struct node_st* cur;
    cur = (*list);
    *list = cur->next;
    free(cur);
    return 0;

}

struct score_st* list_find(struct node_st*list,int id)
{
    struct node_st* cur;
    for(cur = list;cur !=NULL;cur = cur->next)
    {
        if(cur->data.id == id)
        {
            return &cur->data;
        }
    }
    return NULL;
}

void list_destroy(struct node_st* list)
{
    struct node_st* cur;
    for(cur = list;cur !=NULL;cur = list)
    {
        list = cur->next;
        free(cur);
        cur = NULL;
    }
    printf("list destroy \n");
}

makefile

CC = gcc
OBJS = main.o nohand.o
CFLAGS =  -g -Wall -c

all:$(OBJS)
	$(CC) $^  -o $@

%.o:%.c
	$(CC) $^ $(CFLAGS) -o $@	

clean:
	$(RM) -r all $(OBJS)

实战

多项式合并

#include <stdlib.h>
#include <stdio.h>

struct node_st
{
    int coef;
    int expe;
    struct node_st* next;
};

struct node_st* poly_create(int a[][2],int n)
{
    struct node_st* me, *cur;
    me = malloc (sizeof(*me));
    if(me == NULL)
    {
        return NULL;
    }
    cur = me;
    for(int i= 0;i<n;i++)
    {
        struct node_st* newnode;
        newnode = malloc(sizeof(*newnode));
        if(newnode == NULL)
            return NULL;
        
        newnode->coef = a[i][0];
        newnode->expe  = a[i][1];
        newnode->next = NULL;
        cur->next = newnode;
        cur = newnode;
    }
    return me;
};

void poly_show(struct node_st*me)
{
    struct node_st*cur;
    for(cur = me->next; cur!= NULL;cur = cur->next )
    {
        printf("(%d ,%d )",cur->coef,cur->expe);
    }
    printf("\n");
};

void poly_union(struct node_st*p1,struct node_st*p2)
{
    struct node_st*cur1,*cur2,*r;
    r = p1;
    cur1 = p1->next;
    cur2 = p2->next;
    while(cur1 && cur2)
    {
        if(cur1->expe <cur2->expe)
        {
            r->next = cur1;
            r = cur1;
            cur1 = cur1->next;
        }
        else if(cur1->expe > cur2->expe)
        {
            r->next = cur2;
            r = cur2;
            cur2 = cur2->next;
        }
        else  // cur1->expe == cur2->expe
        {
            cur1->coef += cur2->coef;
            if(cur1->coef)
            {
                r->next = cur1;
                r = cur1;
            }
            cur1 = cur1->next;
            cur2 = cur2->next;
        }
    }
    if(cur1 ==NULL)
    {
        r->next = cur2;
    }
    else
        r->next = cur1;

}
int main()
{

    struct node_st* p1,*p2;
    int a[][2] = {{5,0},{2,1},{8,8},{3,16} };
    int b[][2] = {{6,1},{16,6},{-8,8} };

    p1 = poly_create(a,4);
    printf("--------------我是可爱的分界线---------------------\n");
    p2 = poly_create(b,3);
    
    poly_show(p1);
    poly_show(p2);

    poly_union(p1,p2); //将P2合并到p1

    printf("--------------我是可爱的分界线---------------------\n");
    poly_show(p1);
    return 0;
}

循环无头单链表

背景

约瑟夫环(杀人游戏)

举例:某天你去原始森林玩,碰到了一群土著邀请你去玩一个杀人游戏8个人围坐一圈,报数,报到3的人拿根绳吊死,问如何保命,笑到最后

提示:N个人看作是N个链表节点,节点1指向节点2,节点2指向节点3,……,节点N - 1指向节点N,节点N指向节点1,这样就形成了一个环。然后从节点1开始1、2、3……往下报数,每报到M,就把那个节点从环上删除。下一个节点接着从1开始报数。最终链表仅剩一个节点。它就是最终的胜利者。

#include <stdlib.h>
#include <stdio.h>
#define JOSE_NUM 8

struct node_st
{
    int data;
    struct node_st*next;
};

struct node_st* jose_create(int num)
{
    struct node_st *me,*newnode,*cur;
    int i = 1;
    me = malloc(sizeof(*me));
    if(me ==NULL)
        return NULL;
    me->data = i;
    me->next = me;
    i++;
    cur = me; //保证me不变,出错误方便调试
    for(;i<=num;i++)
    {
        newnode = malloc(sizeof(*newnode));
        if(newnode ==NULL)
            exit(1);
        newnode->data = i;
        newnode->next = me;
        cur->next = newnode;
        cur = newnode;
    }
    return me;

}
void jose_show(struct node_st*me)
{
    struct node_st*cur;
    for(cur = me;cur->next!=me; cur = cur->next)
    {
        printf("%d ",cur->data);
    }
    printf("%d \n",cur->data);

}

void jose_kill(struct node_st**me,int num)
{
    struct node_st*cur,*node;
    cur = *me;
    while(cur != cur->next)
    {

    int i = 1;
    while(i<num)
    {
        node = cur;
        cur = cur->next;
        i++;

    }
    printf("%d ",cur->data);
    node->next = cur->next;
    free(cur);

    cur = node->next;
    i = 1;
    }
    *me = cur;
    printf("\n ");

}

int main()
{
    struct node_st*list;
    list = jose_create(JOSE_NUM);
    if(list == NULL)
        return -1;

    jose_show(list);

    int n = 3;
    jose_kill(&list,n);
    jose_show(list);    
    return 0;
}

        双向循环链表

双向循环链表是一种特殊的数据结构,它允许我们在两个方向上遍历链表。与单向链表相比,双向链表在每个节点中增加了两个指针,一个指向前一个节点,另一个指向下一个节点。这种设计使得我们可以更加灵活地处理数据,并且可以更容易地进行插入和删除操作。

全部实现(lib1)

main.c

#include <stdlib.h>
#include <stdio.h>
#include "llist.h"
#include <string.h>

struct score_st
{
    int id;
    char name[32];
    int math;
    int chinese;
};

void print_s(const void *record)
{
    const struct score_st*r = record;
    printf("%d %s %d %d \n",r->id,r->name,r->math ,r->chinese);

}

static int id_cmp(const void*key,const void *record)
{
    const int*k = key;
    const struct score_st* r = record;
    return (*k - r->id);

}
static int name_cmp(const void*key,const void *record)
{
    const char*k = key;
    const struct score_st* r = record;
    return strcmp(k,r->name);
}
int main()
{
#if 1
    LLIST* handler;
    struct score_st temp;
    int ret; 
    handler = llist_create(sizeof(struct score_st) );
    if(handler == NULL)
        exit(1);
    for(int i=0;i<7;i++)
    {
        temp.id = i;
        snprintf(temp.name,sizeof(temp.name),"stu_%d",i);
        temp.math = rand()%100;
        temp.chinese = rand()%100;

        ret = llist_insert(handler,&temp,LLIST_BACKWARD);
        if(ret)
            exit(1);
    }

    llist_travel(handler,print_s);
    
    printf("=====================\n");
    int id = 3;
    struct score *data;
    data = llist_find(handler,&id,id_cmp);
    if(data == NULL)
        printf("没有找到\n");
    else
        print_s(data);

    printf("=====================\n");
    char *del_name = "stu_4";
    ret = llist_delete(handler,del_name,name_cmp);
    if(ret)
        printf("delete error");

    printf("========删除成功============\n");
    llist_travel(handler,print_s);
    llist_destroy(handler);
        
    printf("=====================\n");
#endif  
    return 0;
}

llist.c

#include <stdlib.h>
#include <stdio.h>
#include "llist.h"
#include <string.h>


LLIST* llist_create(int size)
{
    LLIST* new;
    new = malloc(sizeof(*new));
    if(new == NULL)
        return NULL;
    new->size = size;
    new->head.data = NULL;
    new->head.prev = &new->head;
    new->head.next = &new->head;

    return new;

}

int llist_insert(LLIST*ptr,const void*data,int mode)
{
    struct llist_node_st* newnode;

    newnode = malloc(sizeof(*newnode));
    if(newnode == NULL)
        return -1;
    newnode->data = malloc(ptr->size);
    if(newnode->data == NULL)
        return -2;
    memcpy(newnode->data,data,ptr->size);
    
    if(mode == LLIST_FORWARD)
    {
        newnode->prev = &ptr->head;
        newnode->next = ptr->head.next;
    }
    else if (mode == LLIST_BACKWARD)
    {
        newnode->prev = ptr->head.prev;
        newnode->next = &ptr->head;
    }
    else
    {
        // err
        return -3;
    }
    newnode->prev->next = newnode;
    newnode->next->prev = newnode;
    return 0;

}
static struct llist_node_st* find_(LLIST*ptr,const void*key ,llist_cmp* cmp)
{
    struct llist_node_st*cur;
    for(cur = ptr->head.next;cur != &ptr->head; cur = cur->next )
    {
       if(cmp(key,cur->data)== 0)
            break;
    }
    return cur;

}

void* llist_find(LLIST*ptr,const void* key,llist_cmp* cmp)
{
       return  find_(ptr,key,cmp)->data;
}

int llist_delete(LLIST* ptr,const void*key,llist_cmp*cmp)
{
    struct llist_node_st*node;

    node = find_(ptr,key,cmp);
    if(node == &ptr->head )
        return -1;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    free(node->data);
    free(node);
    
    return 0;
}
int llist_fetch(LLIST*ptr,const void*key,llist_cmp*cmp,void* data)
{
    struct llist_node_st* node;
    node = find_(ptr,key,cmp);
    if(node == &ptr->head)
        return -1;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    if(data != NULL)
        memcpy(data,node->data,ptr->size);
    free(node->data);
    free(node);
    return 0;
}

void llist_travel(LLIST*ptr,llist_op* op)
{
    struct llist_node_st *cur;
   for(cur = ptr->head.next;cur != &(ptr->head);cur = cur->next) 
   {
        op(cur->data);
   }
}

void llist_destroy(LLIST*ptr)
{
    struct llist_node_st* cur,*next;
    for(cur = ptr->head.next; cur != &(ptr->head);cur = next)
    {
        next = cur->next;
        free(cur->data);
        free(cur);
    }
    free(ptr);
}



llist.h

#ifndef _LLIST_H__
#define _LLIST_H__

#define LLIST_FORWARD  1
#define LLIST_BACKWARD 2
typedef void llist_op(const void *);   // typedef声明了一个函数类型,可以用 list_op *p生成一个函数指针
typedef int llist_cmp(const void *,const void* );  


struct llist_node_st
{
    void *data;
    struct llist_node_st* prev;
    struct llist_node_st* next;
};

typedef struct  // llist_node_head
{
    int size;
    struct llist_node_st head;
}LLIST;

LLIST* llist_create(int size);
#if 1 
int llist_insert(LLIST*,const void*data,int mode);

void * llist_find(LLIST*,const void*,llist_cmp*);

int llist_delete(LLIST*,const void*key,llist_cmp*);

int llist_fetch(LLIST*,const void*key,llist_cmp*,void* data);

void llist_travel(LLIST*,llist_op* );

void llist_destroy(LLIST*);

#endif 

#endif

使用变长结构体实现(lib2)

 10 struct llist_node_st
 11 {
 12     struct llist_node_st* prev;
 13     struct llist_node_st* next;
 14     char  data[1];  //变长结构体,去data就是一块地址 C语言空结构体占用0字节,C++占用1字节。
 15     
 16 };


使用变长结构体,用户输入的数据我们可以通过data来获取到,方便使用和计算

llist.c

#include <stdlib.h>
#include <stdio.h>
#include "llist.h"
#include <string.h>


LLIST* llist_create(int size)
{
    LLIST* new;
    new = malloc(sizeof(*new));
    if(new == NULL)
        return NULL;
    new->size = size;
    new->head.prev = &new->head;
    new->head.next = &new->head;

    return new;

}

int llist_insert(LLIST*ptr,const void*data,int mode)
{
    struct llist_node_st* newnode;

    newnode = malloc(sizeof(*newnode)+ ptr->size);
    if(newnode == NULL)
        return -1;
    memcpy(newnode->data,data,ptr->size);
    
    if(mode == LLIST_FORWARD)
    {
        newnode->prev = &ptr->head;
        newnode->next = ptr->head.next;
    }
    else if (mode == LLIST_BACKWARD)
    {
        newnode->prev = ptr->head.prev;
        newnode->next = &ptr->head;
    }
    else
    {
        // err
        return -3;
    }
    newnode->prev->next = newnode;
    newnode->next->prev = newnode;
    return 0;

}
static struct llist_node_st* find_(LLIST*ptr,const void*key ,llist_cmp* cmp)
{
    struct llist_node_st*cur;
    for(cur = ptr->head.next;cur != &ptr->head; cur = cur->next )
    {
       if(cmp(key,cur->data)== 0)
            break;
    }
    return cur;

}

void* llist_find(LLIST*ptr,const void* key,llist_cmp* cmp)
{
        struct llist_node_st*node;

       node = find_(ptr,key,cmp);
       if(node == &ptr->head)
            return NULL;
        return node->data;

}

int llist_delete(LLIST* ptr,const void*key,llist_cmp*cmp)
{
    struct llist_node_st*node;

    node = find_(ptr,key,cmp);
    if(node == &ptr->head )
        return -1;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    free(node);
    
    return 0;
}
int llist_fetch(LLIST*ptr,const void*key,llist_cmp*cmp,void* data)
{
    struct llist_node_st* node;
    node = find_(ptr,key,cmp);
    if(node == &ptr->head)
        return -1;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    if(data != NULL)
        memcpy(data,node->data,ptr->size);
    free(node);
    return 0;
}

void llist_travel(LLIST*ptr,llist_op* op)
{
    struct llist_node_st *cur;
   for(cur = ptr->head.next;cur != &(ptr->head);cur = cur->next) 
   {
        op(cur->data);
   }
}

void llist_destroy(LLIST*ptr)
{
    struct llist_node_st* cur,*next;
    for(cur = ptr->head.next; cur != &(ptr->head);cur = next)
    {
        next = cur->next;
        free(cur);
    }
    free(ptr);
}



llist.h

#ifndef _LLIST_H__
#define _LLIST_H__

#define LLIST_FORWARD  1
#define LLIST_BACKWARD 2
typedef void llist_op(const void *);   // typedef声明了一个函数类型,可以用 list_op *p生成一个函数指针
typedef int llist_cmp(const void *,const void* );  


struct llist_node_st
{
    struct llist_node_st* prev;
    struct llist_node_st* next;
    char  data[1];  //变长结构体,去data就是一块地址 C语言空结构体占用0字节,C++占用1字节。

};

typedef struct  // llist_node_head
{
    int size;
    struct llist_node_st head;
}LLIST;

LLIST* llist_create(int size);
#if 1 
int llist_insert(LLIST*,const void*data,int mode);

void * llist_find(LLIST*,const void*,llist_cmp*);

int llist_delete(LLIST*,const void*key,llist_cmp*);

int llist_fetch(LLIST*,const void*key,llist_cmp*,void* data);

void llist_travel(LLIST*,llist_op* );

void llist_destroy(LLIST*);

#endif 

#endif

使用类封装来实现

 typedef struct   llist_head
 19 {           
 20     int size;
 21     struct llist_node_st head;
 22     int (*insert)(struct llist_head*,const void*,int);
 23     void* (*find)(struct llist_head*,const void*,llist_cmp*);
 24     int (*del)(struct llist_head*,const void*,llist_cmp*);
 25     int (*fetch)(struct llist_head*,const void*,llist_cmp*i,void *);
 26     void (*travel)(struct llist_head*,llist_op *);
 27     
 28     
 29 }LLIST; 

  
    int (*insert)(struct llist_head*,const void*,int);
  7 void (*find)(struct llist_head*,const void*,llist_cmp*);
  8 int (*del)(struct llist_head*,const void*,llist_cmp*);
  9 int (*fetch)(struct llist_head*,const void*,llist_cmp*i,void *);
 10 int (*travel)(struct llist_head*,llist_op *);
 11 
 12 
 13 LLIST* llist_create(int size)
 14 {   
 15     LLIST* new;
 16     new = malloc(sizeof(*new));
 17     if(new == NULL)
 18         return NULL;
 19     new->size = size;
 20     new->head.prev = &new->head;
 21     new->head.next = &new->head;
 22     
 23     new->insert = llist_insert;
 24     new->del = llist_delete;
 25     new->find = llist_find;
 26     new->fetch = llist_fetch;
 27     new->travel = llist_travel;
 28     return new;
 29 
 30 }

实现数据隐藏(lib3)

llist.c

#include <stdlib.h>
#include <stdio.h>
#include "llist.h"
#include <string.h>

struct llist_node_st
{
    struct llist_node_st* prev;
    struct llist_node_st* next;
    char  data[1];  //变长结构体,去data就是一块地址 C语言空结构体占用0字节,C++占用1字节。

};

struct  llist_head_st
{
    int size;
    struct llist_node_st head;
};



LLIST* llist_create(int size)
{
    struct llist_head_st* new;
    new = malloc(sizeof(*new));
    if(new == NULL)
        return NULL;
    new->size = size;
    new->head.prev = &new->head;
    new->head.next = &new->head;

    return new;

}

int llist_insert(LLIST*p,const void*data,int mode)
{
    struct llist_node_st* newnode;
    struct llist_head_st*ptr = p;

    newnode = malloc(sizeof(*newnode)+ ptr->size);
    if(newnode == NULL)
        return -1;
    memcpy(newnode->data,data,ptr->size);
    
    if(mode == LLIST_FORWARD)
    {
        newnode->prev = &ptr->head;
        newnode->next = ptr->head.next;
    }
    else if (mode == LLIST_BACKWARD)
    {
        newnode->prev = ptr->head.prev;
        newnode->next = &ptr->head;
    }
    else
    {
        return -3;
    }
    newnode->prev->next = newnode;
    newnode->next->prev = newnode;
    return 0;

}
static struct llist_node_st* find_(struct llist_head_st*ptr,const void*key ,llist_cmp* cmp)
{
    struct llist_node_st*cur;
    for(cur = ptr->head.next;cur != &ptr->head; cur = cur->next )
    {
       if(cmp(key,cur->data)== 0)
            break;
    }
    return cur;

}

void* llist_find(LLIST*p,const void* key,llist_cmp* cmp)
{
        struct llist_node_st*node;
        struct llist_head_st *ptr = p;

       node = find_(ptr,key,cmp);
       if(node == &ptr->head)
            return NULL;
        return node->data;

}

int llist_delete(LLIST* p,const void*key,llist_cmp*cmp)
{
    struct llist_node_st*node;
    struct llist_head_st *ptr = p;

    node = find_(ptr,key,cmp);
    if(node == &ptr->head )
        return -1;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    free(node);
    
    return 0;
}
int llist_fetch(LLIST*p,const void*key,llist_cmp*cmp,void* data)
{
    struct llist_node_st* node;
    struct llist_head_st *ptr = p;

    node = find_(ptr,key,cmp);
    if(node == &ptr->head)
        return -1;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    if(data != NULL)
        memcpy(data,node->data,ptr->size);
    free(node);
    return 0;
}

void llist_travel(LLIST*p,llist_op* op)
{
    struct llist_head_st* ptr = p;
    struct llist_node_st *cur;
   for(cur = ptr->head.next;cur != &(ptr->head);cur = cur->next) 
   {
        op(cur->data);
   }
}

void llist_destroy(LLIST*p)
{
    struct llist_head_st *ptr = p;
    struct llist_node_st* cur,*next;
    for(cur = ptr->head.next; cur != &(ptr->head);cur = next)
    {
        next = cur->next;
        free(cur);
    }
    free(ptr);
}



llist.h

#ifndef _LLIST_H__
#define _LLIST_H__

#define LLIST_FORWARD  1
#define LLIST_BACKWARD 2

typedef void LLIST;

typedef void llist_op(const void *);   // typedef声明了一个函数类型,可以用 list_op *p生成一个函数指针
typedef int llist_cmp(const void *,const void* );  



LLIST* llist_create(int size);

int llist_insert(LLIST*,const void*data,int mode);

void * llist_find(LLIST*,const void*,llist_cmp*);

int llist_delete(LLIST*,const void*key,llist_cmp*);

int llist_fetch(LLIST*,const void*key,llist_cmp*,void* data);

void llist_travel(LLIST*,llist_op* );

void llist_destroy(LLIST*);


#endif

内核源码实现-双向环链(kernal)

路径:/usr/src/linux-headers-5.15.0-105-generic/include/linux

内核中头宏定义的实现一般都是前加下划线,我们最好定义宏的时候采用后加下划线,来防止定义冲突。

使用命令查找container_of函数的实现:grep "#define container_of(" -R .

main.c

#include <stdlib.h>
#include <stdio.h>
#include "list.h"

struct score_st
{
    int id;
    char name[32];
    int math;
    int chinese;
    struct list_head node;
};

static void print_s(struct score_st *d)
{
    printf("%d %s %d %d \n",d->id,d->name,d->math,d->chinese);
}

int main()
{
    struct score_st *data_ptr;
    struct list_head*  cur;
   
   LIST_HEAD(head);

    for(int i=0;i<7;i++)
    {
        data_ptr = malloc(sizeof(*data_ptr));
        if(data_ptr == NULL)
            return -1;
        data_ptr->id = i;
        data_ptr->math = rand()%100;
        data_ptr->chinese = rand()%100;
        snprintf(data_ptr->name,32,"stu_%d",i);
        list_add(&data_ptr->node,&head); 
    
    }
    __list_for_each(cur,&head)
    {
       data_ptr = list_entry(cur,struct score_st,node);
        print_s(data_ptr);
    }
    printf("=============================\n");
    __list_for_each(cur,&head)
    {
       data_ptr = list_entry(cur,struct score_st,node);
       if(data_ptr->id = 5)
            break;
    }
    if(cur == &head)
    {
        printf("no find \n");
    }
    else
        print_s(data_ptr);

    return 0;
}




list.h

#ifndef LINUX_LIST_H
#define LINUX_LIST_H

struct list_head
{
    struct list_head* prev;
    struct list_head* next;

};

#define LIST_HEAD_INIT(name) {&(name),&(name)}

#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)

static inline void __list_add(struct list_head *new,
        struct list_head *prev,
        struct list_head *next)
{
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;

}

static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
}


#define __list_for_each(pos, head) \
    for (pos = (head)->next; pos !=(head); pos = pos->next)
#if 0
ptr->cur;
type->struct score_st
member->node 
#endif 

#define offsetof(TYPE, MEMBER)  ((size_t) &((TYPE *)0)->MEMBER)


#if 1
#define container_of(ptr,type,member) \
    ({ (type*)( (char*)ptr - offsetof(type,member)  ); })

#else

#define container_of(ptr,type,member)({     \
    const typeof( ((type*)0)->member) *__mptr = (ptr); \
    (type*)( (char*)__mptr - offsetof(type,member) ); })
#endif 

#define list_entry(ptr, type, member) container_of(ptr,type,member)







#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/600707.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分析错误ValueError: could not determine the shape of object type ‘Series‘

这个错误提示 ValueError: could not determine the shape of object type Series 通常发生在尝试将 pandas 的 Series 直接转换为 PyTorch 的 tensor 时&#xff0c;尤其是当 Series 的数据类型不明确或者包含非数值类型的数据时。为了修正这个问题&#xff0c;确保在转换之前…

利用Jenkins完成Android项目打包

问题和思路 目前存在的问题 打包操作由开发人员完成&#xff0c;这样开发进度容易被打断。 解决问题的思路 将打包操作交测试/产品/开发人员来完成&#xff0c;主要是测试/开发。 按照以上的思路&#xff0c;那么JenkinsGradle的解决方案是比较经济的&#xff0c;实现起来…

跟随Facebook的足迹:社交媒体背后的探索之旅

在当今数字化时代&#xff0c;社交媒体已经成为了人们日常生活中不可或缺的一部分。而在这庞大的社交媒体网络中&#xff0c;Facebook作为其中的巨头&#xff0c;一直在引领着潮流。从创立之初的一个大学社交网络到如今的全球性平台&#xff0c;Facebook的发展历程承载了无数故…

雷军-2022.8小米创业思考-6-互联网七字诀之专注:有所为,有所不为;克制贪婪,少就是多;一次解决一个最迫切的需求

第六章 互联网七字诀 专注、极致、口碑、快&#xff0c;这就是我总结的互联网七字诀&#xff0c;也是我对互联网思维的高度概括。 专注 从商业角度看&#xff0c;专注就是要“把鸡蛋尽量放在一个篮子里”。这听起来似乎有些不合理&#xff0c;大家的第一反应可能是“风险会不会…

stripe支付

使用第一个示例 1、示例中的PRICE_ID需要去Stripe控制台->产品目录创建产品 1、 添加产品 2、点击查看创建的产品详情 4、这个API ID就是demo中的PRICE_ID 注意&#xff1a;需要注意的是&#xff0c;测试模式和生产模式中的 $stripeSecretKey 需要对应上。简而言之就是不能生…

【嵌入式必读】一文彻底理解PID自整定及PID自整定代码设计

文章目录 1. 前言2. PID简介3. 常用的PID自整定方法3.1 临界度比例法3.2 衰减曲线法 4. 继电反馈整定法原理4.1 继电反馈自整定的基本思想4.2 继电反馈自整定原理 5. 算法设计5.1 振荡的生成5.2 提取出临界周期 T c T_c Tc​和振荡波形幅值 A A A5.3 计算出PID参数 6 原代码6.1…

【Linux】Docker 安装部署 Nacos

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 【Linux】Docker 安装部署 Nacos docker搜索na…

如何获得一个Oracle 23ai数据库(Virtual Appliance)

准确的说&#xff0c;是Oracle 23ai Free Developer版&#xff0c;因为企业版目前只在云上&#xff08;OCI和Azure&#xff09;和ECC上提供。 方法包括3种&#xff0c;本文介绍第1种&#xff1a; Virtual ApplianceRPM安装Docker 从此处下载虚拟机。 可以看到虚拟机需要4G内…

武汉星起航:精准布局,卓越服务——运营交付团队领跑亚马逊

在全球电商浪潮中&#xff0c;亚马逊平台以其独特的商业模式和全球化的市场布局&#xff0c;吸引了无数商家和创业者的目光。在这个充满机遇的市场中&#xff0c;武汉星起航电子商务有限公司凭借其专业的运营交付团队&#xff0c;以其独特的五对一服务体系和精准的战略布局&…

CSS学习笔记之基础教程(一)

1、CSS语法 CSS 规则集&#xff08;rule-set&#xff09;由选择器和声明块组成&#xff1a; 选择器指向您需要设置样式的 HTML 元素。 声明块包含一条或多条用分号分隔的声明。 每条声明都包含一个 CSS 属性名称和一个值&#xff0c;以冒号分隔。 多条 CSS 声明用分号分隔…

【Linux】文件内容相关的命令,补充:管道符

1、查看文件内容 &#xff08;1-1&#xff09;查看文件内容&#xff1a;cat&#xff0c;tac&#xff0c;head&#xff0c;tail 查看文件内容cat 文件名查看文件内容并显示行号cat -n 文件名倒着查看文件内容&#xff08;从最后一行开始&#xff09;tac 文件名查看文件前10行…

Pycharm远程同步的mapping与sync

用Pycharm进行项目远程部署的时候会遇到两个同步文件&#xff0c;一个是点击 tools—>deployment—>configration——>mapping 一个是链接虚拟环境的时候会有一个sync&#xff0c;那么这两种同步有什么区别呢&#xff1f; 区别就是&#xff0c;2包括1&#xff0c;要用…

GORM的常见命令

文章目录 一、什么是GORM&#xff1f;二、GORM连接mysql以及AutoMigrate创建表三、查询1、检索此对象是否存在于数据库&#xff08;First,Take,Last方法&#xff09;2、Find()方法检索3、根据指定字段查询 四、更新1、Save() 保存多个字段2、更新单个字段 五、删除 一、什么是G…

QT截图程序,可多屏幕截图

截图程序&#xff0c;支持多屏幕时跨屏幕截图。截图使用setMask达到镂空效果&#xff0c;截图后会有预览和保存功能。截图时按下Esc可退出。 mainwindow.ui mainwindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> …

docker jenkins 部署springboot项目

1、创建jenkins容器 1&#xff0c;首先&#xff0c;我们需要创建一个 Jenkins 数据卷&#xff0c;用于存储 Jenkins 的配置信息。可以通过以下命令创建一个数据卷&#xff1a; docker volume create jenkins_data启动 Jenkins 容器并挂载数据卷&#xff1a; docker run -dit…

搞定 TS 装饰器,让你写 Node 接口更轻松

前言 亲爱的小伙伴&#xff0c;你好&#xff01;我是 嘟老板。你是否用过 TypeScript 呢&#xff1f;对 装饰器 了解多少呢&#xff1f;有没有实践应用过呢&#xff1f;今天我们就来聊聊 装饰器 的那点事儿&#xff0c;看看它有哪些神奇的地方。 什么是装饰器 咱们先来看一段…

密码学《图解密码技术》 记录学习 第十三章

目录 第十三章 13.1 本章学习的内容 13.2 PGP 简介 13.2.1 什么是 PGP 13.2.2 关于 OpenPGP 13.2.3关于GNU Privacy Guard 13.2.4 PGP 的功能 公钥密码 数字签名 单向散列函数 证书 压缩 文本数据 大文件的拆分和拼合 13.3 生成密钥对 13.4 加密与解密 13.4.1 加密 生成…

Qt | QComboBox(组合框)

01、上节回顾 Qt 基础教程合集02、QComBox 一、QComboBox 类(下拉列表、组合框) 1、QComboBox 类是 QWidget 类的直接子类,该类实现了一个组合框 2、QComboBox 类中的属性 ①、count:const int 访问函数:int count() const; 获取组合框中的项目数量,默认情况下,对于空…

js 图片渐变

1. 点击图片&#xff0c;使其渐变为另一张图片 通过定义keyframes来创建一个淡入淡出的动画效果。当图片被点击时&#xff0c;先添加淡出动画使图片透明度从0渐变到1&#xff0c;然后在1秒后切换图片源并添加淡入动画使新图片透明度从0渐变到1&#xff0c;实现图片渐变效果。 …

光伏SRM供应商管理解决方案

供应商管理是光伏企业中重要的一环&#xff0c;通过SRM管理供应商&#xff0c;可以提高产品质量&#xff0c;降低采购成本&#xff0c;并集成供应链&#xff0c;提高核心竞争力。 一、搭建管理系统 分为供应商和商户&#xff0c;供应商需要完善基本信息、类别、等级、产品概要…
最新文章