链表--单链表

引言:

链表是一种常用的数据结构,在C语言中提供了一种灵活且高效的数据管理方式,它适用于那些需要频繁插入和删除操作的场景。链表的每个节点包含数据和指向下一个节点的指针,这种结构使得链表能够有效地利用内存,并且提供快速的插入和删除能力。然而,由于是动态分配的,访问链表中的元素通常比访问数组中的元素慢,因为必须从头节点开始按顺序访问。链表的这种特性使其成为许多复杂数据结构和算法的基础。 

一.链表的概念和结构

链表若干个结点组成。每个结点都有两部分组成:数据域和指针域。数据域存储结点的数据,而指针域则指向下一个结点。

struct node    
{
    int value;   //数据域:存放链表数据
    struct node* next;  //指针域:存放指向下一个结点的地址
                        //在链表遍历的过程中,我们根据前一个结点的指针域中的那个地址
                        //找到下一个结点,就这样一个接一个往下遍历,
                        //进行增删改查等一系列基础操作。
};

二.链表的创建和初始化

通过定义一个结构体来表示链表的结点,在结构体中含有指向结构体的指针,通过指针变量将结点链接起来。

#include <stdio.h>
#include <stdlib.h>
//单链表
struct node    
{
    int value;   //数据域:存放链表数据
    struct node* next;  //指针域:存放指向下一个结点的地址
};
int main()
{
    struct node* head, * list, * tmp;
            //头结点   头指针   结点指针
    return 0;
}

开始时list指向头结点head,struct node*tmp通过malloc函数不断地创建结点,list->next指向tmp(下一个结点),同时也需要把tmp的地址赋给list,然后不断重复以上操作一个单链表就形成了。

1.带头结点的单链表中,头指针list指向头节点,头结点的值域不含任何信息,从
头结点的后继节点开始存储数据信息(头指针list始终不等于NULL,list->next
等于NULL的时候,链表为空)
2.不带头结点的单链表的头指针list直接指向开始结点(当list等于NULL的时候,
链表为空)
3.两者最明显的区别为,具有头结点的单链表头结点仅存储一些描述链表属性的信息,而不带头节点的单链表的所有结点都存储信息。
结点是内存中一块动态开辟的存储空间,只有一个地址来表示它的存在。因此,
在分配链表结点空间的时候,同时定义一个指针来存储这片空间的地址,即指针指
向节点。(常用指针的名称来作为结点的名称)

 在链表的创建过程中需要使用malloc函数.它是C语言中的一个动态内存分配函数.用于在堆上分配指定大小的内存块。

在上述链表创建过程中使用malloc函数时需要注意malloc函数是否在内存中开辟出了内存块

int main()
{
    int data[10] = { 1,2,3,4,5,6,7,8,9,10 };

    //创建头结点
    struct node* head = (struct node*)malloc(sizeof(struct node));
    //检查malloc是否成功分配内存
    if (head == NULL)
    {
        printf("Memory allocation failed\n");
        exit(1);
    }
    head->next = NULL;
    //头指针的创建
    struct node* list ;
    list = head;
    
    //创建单链表其他节点
    int i=0;
    struct node* tmp;
    while (i < 10)
    {
        tmp = (struct node*)malloc(sizeof(struct node));
        //检查malloc是否成功分配内存
        if (tmp == NULL)
        {
            printf("Memory allocation failed\n");
            exit(1);
        }
        else
        {
            //对链表结点的数据域赋值
            tmp->value = data[i];

            tmp->next = NULL;
            list->next = tmp;
            list = tmp;
        }
        i++;
    }
    struct node* p;
    p = head;
    while (p->next)
    {
        printf("%d\n", p->next->value);
        p = p->next;
    }
}

在链表创建和初始化完成后,我们需要去验证一下自己的学习和实践成果。那么我们就可以去打印一下链表数据域中初始化存入的数值,如果一致,恭喜我们今天又掌握了"汪洋大海中的一滴水"

 显然,我们刚才的创建和初始化是成功的,那么接下来我们就需要去探索一下单链表的功能和作用了。

三.单链表的功能和作用

1.删除结点

单链表的节点删除可以通过以下步骤实现:

遍历链表,找到要删除的节点的前一个节点。
将前一个节点的指针指向要删除节点的下一个节点。
释放要删除节点的内存。

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

typedef struct Node {
    int val;
    struct Node* next;
} Node;

Node* delete_node(Node* head, int val) {
    if (head == NULL) {
        return NULL;
    }

    if (head->val == val) {
        Node* new_head = head->next;
        free(head);
        return new_head;
    }

    Node* prev = head;
    Node* cur = head->next;
    while (cur != NULL) {
        if (cur->val == val) {
            prev->next = cur->next;
            free(cur);
            break;
        }
        prev = cur;
        cur = cur->next;
    }

    return head;
}

示例中,我们定义了一个`Node`结构体表示单链表的节点,其中包含一个整数值和一个指向下一个节点的指针。`delete_node`函数接受一个头节点和一个要删除的值,然后删除具有该值的节点。如果头节点是要删除的节点,我们将头节点的指针指向下一个节点,并释放头节点的内存。否则,我们遍历链表,找到要删除节点的前一个节点,并将前一个节点的指针指向要删除节点的下一个节点,然后释放要删除节点的内存。最后,返回修改后的头节点。

2.插入结点

单链表的节点插入可以通过以下步骤实现:

单链表的节点插入可以通过以下步骤实现:

 创建一个新的节点,并设置其值。
 找到要插入的位置的前一个节点。
 将新节点的指针指向前一个节点的下一个节点。
 将前一个节点的指针指向新节点。

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

typedef struct Node {
    int val;
    struct Node* next;
} Node;

Node* insert_node(Node* head, int val, int position) {
    Node* new_node = (Node*)malloc(sizeof(Node));
    new_node->val = val;
    if (position == 0) {
        new_node->next = head;
        return new_node;
    }

    Node* prev = NULL;
    Node* cur = head;
    for (int i = 0; i < position; i++) {
        if (cur == NULL) {
            break;
        }
        prev = cur;
        cur = cur->next;
    }

    if (prev != NULL) {
        prev->next = new_node;
    }
    new_node->next = cur;

    return head;
}

示例中,`ListNode`类表示单链表的节点,`insert_node`函数接受一个头节点、一个要插入的值和一个插入位置,然后插入一个新节点。如果要在链表的开头插入节点,我们将新节点的指针指向头节点,并将新节点作为新的头节点返回。否则,我们遍历链表,找到要插入位置的前一个节点,并将新节点的指针指向前一个节点的下一个节点,然后将前一个节点的指针指向新节点。最后,返回修改后的头节点。

3.清空链表

单链表的清空可以通过以下步骤实现:

遍历链表,释放每个节点的内存。
将头节点指针设置为NULL。

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

typedef struct Node {
    int val;
    struct Node* next;
} Node;

void clear_list(Node* head) {
    Node* cur = head;
    while (cur != NULL) {
        Node* next = cur->next;
        free(cur);
        cur = next;
    }
}

4.其他功能:

求链表长度:遍历链表并计数,直到最后一个节点,即可得到链表的长度
查找结点:通过遍历链表寻找含有特定数据的节点,常用于搜索操作
输出链表:通过从头到尾遍历链表,输出每个节点的数据域内容
修改结点值:通过遍历链表找到对应的节点,然后修改其数据域的值

结语:

谢谢你看完我的文章,喜欢我的文章的话就给我点个赞再走吧!

下次见!拜拜┏(^0^)┛

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值