C语言单链表

本文详细介绍了C语言中单链表的结构、初始化、打印、头插法、尾插法、按位查找与插入、按位删除以及链表销毁等基本操作。
摘要由CSDN通过智能技术生成

单链表

1.单链表结构体

typedef struct LNode
{
    ElemType data;
    struct LNode *next;
} LNode, *LinkList;

LNOde *L; 可读性不强,代表的是一个指向头结点的指针 但通常不知道它是一个结点还是一个指向头结点的指针

LinkList L; 可读性较强,代表的是一个指向头结点的指针 主要可以和一个新的结点,和指向头结点的指针区别开来

2.初始化单链表

void InitLinkList(LinkList L)
{
    L->data = 0; // 存储表长
    L->next = NULL;
}

在初始化链表的同时其实是在初始化头结点,我们需要在main函数里面给头结点分配内存空间

int main()
{
	LinkList L;
	L = (LNode*)malloc(sizeof(LNode)); // 分配内存空间
	return 0;
}

3.打印单链表

void PrintfLinkList(LinkList L)
{
    LNode *p;
    p = L;
    while (p->next != NULL)
    {
        p = p->next;
        printf("%d-->", p->data);
    }
    printf("NULL\n");
}

打印的本质是利用循环,一直找到结点的下一个为NULL的情况下,停止循环

4.头插法

void InsertLinkListHeadNode(LinkList L)
{
    LNode *newNode;        // 新节点
    ElemType newNode_data; // 新节点的数据域

    // 设置9999为结束条件
    printf("please enter to number(9999 mean exit)\n");
    scanf("%d", &newNode_data);
    while (newNode_data != 9999)
    {
        newNode = (LNode *)malloc(sizeof(LNode));
        newNode->next = L->next;
        L->next = newNode;
        newNode->data = newNode_data;
        L->data++; // 表长+1
        printf("please enter to number(9999 mean exit)\n");
        scanf("%d", &newNode_data);
    }
}

以9999为结束循环的条件,不断在头部插入结点

5.尾插法

void InsertLinkListTailNode(LinkList L)
{
    LNode *newNode;
    ElemType newNode_data;
    LNode *TailNode = L; // 代表指向尾节点的指针

    // 循环找到最后一个节点
    while (TailNode->next != NULL)
    {
        TailNode = TailNode->next;
    }

    printf("please enter a number(9999 mean exit)\n");
    scanf("%d", &newNode_data);
    while (newNode_data != 9999)
    {
        newNode = (LNode *)malloc(sizeof(LNode));
        newNode->data = newNode_data;
        TailNode->next = newNode;
        newNode->next = NULL;
        TailNode = newNode; // 让插入尾部的节点变成尾节点
        L->data++;
        printf("please enter a number(9999 mean exit)\n");
        scanf("%d", &newNode_data);
    }
}

因为结点是从头结点开始的,所以要通过循环一直找到最后的一个结点,插入结点后,插入的这个结点就成了新的尾结点

6.按位查找

LNode *GetElem(LinkList L, int i)
{
    if (i == 0)
    {
        printf("The LinkList's element you are looking for does not exist\nReturn the head pointer of the LinkList!\n ");
        return L;
    }
    // 数组下标超界
    else if (i < 0 || i > L->data)
    {
        printf("The LinkList's element you are looking for does not exist\nReturn NULL!\n ");
        return NULL;
    }
    else
    {
        LNode *p;
        p = L;
        for (int j = 0; j < i; j++)
        {
            p = p->next;
        }
        return p;
    }
}

7.按值查找

// 单链表按值查找
LNode *LocateELem(LinkList L, ElemType e)
{
    if (!L->next)
    {
        printf("LinkList is empty\n");
        return NULL;
    }
    LNode *p;
    p = L;
    while (p->next != NULL)
    {
        p = p->next;
        if (p->data == e)
            return p;
    }
    printf("not find number\n");
    return NULL;
}

8.按位插入

// 单链表按位插入
ElemType LocatInsertLinkList(LinkList L, int i, ElemType e)
{
    if (i < 1 || i > L->data + 1)
    {
        printf("The position of the element to be inserted is invalid\n");
        return 0;
    }
    LNode *p = GetElem(L, i - 1);
    LNode *newNode = (LNode *)malloc(sizeof(LNode));
    newNode->data = e;
    newNode->next = p->next;
    p->next = newNode;
    L->data++;
    return 1;
}

通过按位查找找到想查找的位置的前一个位置,在进行插入操作

9.按位删除

// 单链表按位删除
ElemType LocateDeleteLinkList(LinkList L, int i, ElemType *e)
{
    if (!L || !L->next)
    {
        printf("The LinkList is empty\n");
        *e = 9999;
        return 0;
    }
    if (i < 1 || i > L->data)
    {
        printf("The position of the element to be deleted is invalid\n");
        *e = 9999;
        return 0;
    }
    LNode *p = GetElem(L, i - 1);
    LNode *q = p->next;
    p->next = q->next;
    *e = q->data;
    free(q);
    L->data--;
    return 1;
}

也是通过GetElem函数找到前一个位置,在来改变结点的指向,并且通过free函数来将结点释放掉,达到删除结点的操作

10.销毁链表

void DestroyLinkList(LinkList L)
{
    LNode *p, *q;
    p = L;
    while (p != NULL)
    {
        q = p->next;
        free(p);
        p = q;
    }
    L = NULL;
    printf("LinkList destroyed\n");
}

11.测试

int main()
{
    LinkList L;                         // L是一个指向头节点的指针
    L = (LNode *)malloc(sizeof(LNode)); // 分配内存空间
    InitLinkList(L);                    // 初始化链表
    // printf("%d \n", L->data);        // 0
    
    // 测试头插法
    // InsertLinkListHeadNode(L);
    // PrintfLinkList(L);

    // 测试尾插法
    InsertLinkListTailNode(L);
    PrintfLinkList(L);

    // printf("%d\n", GetElem(L, 3)->data);
    // printf("%d\n", LocateELem(L, 3)->data);

    // 测试按位插入
    // LocatInsertLinkList(L, 1, 9999);
    // PrintfLinkList(L);
    // PrintfLinkList(L);

    // 测试按位删除
    // ElemType e;
    // LocateDeleteLinkList(L, 2, &e);
    // printf("e=%d\n", e);
    // PrintfLinkList(L);

    // 测试销毁单链表
    DestroyLinkList(L);
    
    // 注意:销毁链表后在打印链表会报错
    PrintfLinkList(L); // error
    return 0;
}
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) C语言下的单链表,可以增加,删除,查找,销毁节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值