引言:
链表是一种常用的数据结构,在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^)┛
标签:Node,node,结点,单链,--,next,链表,节点 From: https://blog.csdn.net/2301_79695216/article/details/139474029