一、概述
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。一个单链表结点由以下两个域构成。
数据域 | 指针域 |
---|---|
data | next |
其中数据域data存放该结点的数据域的值,指针域存放该结点的后继结点的地址信息。
如果想要去跑下面代码的实现,请先加上头文件和结构体声明以及全局变量,这里也附带了一个测试用的主函数。
#include<stdio.h>
#include<stdlib.h>
// 结构体所需的内存空间
#define LEN sizeof(struct Node)
// 输入错误时的返回值
#define ERROR_INPUT -1
// 定义单链表的结构体,存放数据域和指针域
struct Node
{
int data;
struct Node *next;
};
// 结点的总数
int N;
// 函数声明
struct Node *create();
void print(struct Node *head);
int insert(int k, int data, struct Node *head);
int delete(int k, struct Node *head);
int find(int k, struct Node *head);
int search(int item, struct Node *head);
int main()
{
struct Node *p;
p = (struct Node*)malloc(LEN);
// 测试
p = create();
insert(1, 22, p);
delete(3, p);
printf("%d\n",find(2, p));
printf("%d\n",search(3, p));
print(p);
return 0;
}
// 创建一个单链表,返回头指针
struct Node *create()
{
struct Node *head;
struct Node *p1;
struct Node *p2;
N = 0;
p1 = (struct Node*)malloc(LEN);
// 当输入的最后一个值为0时结束
printf("请依次输入每个结点的数据域的值,输入0结束\n");
scanf("%d", &p1->data);
head = NULL;
while (p1->data != 0) {
N = N + 1;
if (N == 1) {
head = p1;
} else {
p2->next = p1;
}
p2 = p1;
p1 = (struct Node*)malloc(LEN);
scanf("%d", &p1->data);
}
// 最后一个结点的next存NULL
p2->next = NULL;
return head;
}
二、单链表的操作
下面是关于单链表的基本操作,在进行这些操作前,请先添加概述里面的代码,并按需调整。其中插入,删除,存取,查找等操作需要建立在创建单链表的操作之下,才可实现。最后的遍历输出单链表所有结点的数据域是将当前的列表的所有结点值打印出来的一个可视化的操作。
1. 创建单链表
// 使用动态列表的方法创建并初始化一个单链表,返回头指针
struct Node *create()
{
struct Node *head;
struct Node *p1;
struct Node *p2;
N = 0;
p1 = (struct Node*)malloc(LEN);
// 当输入的最后一个值为0时结束
printf("请依次输入每个结点的数据域的值,输入0结束\n");
scanf("%d", &p1->data);
head = NULL;
while (p1->data != 0) {
N = N + 1;
if (N == 1) {
head = p1;
} else {
p2->next = p1;
}
p2 = p1;
p1 = (struct Node*)malloc(LEN);
scanf("%d", &p1->data);
}
// 最后一个结点的next存NULL
p2->next = NULL;
return head;
}
2. 插入操作
// 第k个结点后插入数据域为data的结点
int insert(int k, int data, struct Node *head)
{
struct Node *p;
struct Node *s;
int i;
// 初始化
s = (struct Node*)malloc(LEN);
p = head;
i = 0;
// k是否合法
if (k < 0) {
printf("插入不合法\n");
return ERROR_INPUT;
}
// p指向第k个结点
while (p != NULL && i < k-1) {
p = p->next;
i++;
}
// 插入
s->data = data;
s->next = p->next;
p->next = s;
// 更新节点总数
N++;
}
3. 删除操作
// 删除链表中的第k个结点
int delete(int k, struct Node *head)
{
struct Node *p;
struct Node *q;
int i;
p = head;
i = 1;
if (k < 1) {
printf("删除不合法\n");
return ERROR_INPUT;
}
// 找第k-1个结点
while (p->next != NULL && i < k-1) {
p = p->next;
i++;
if (p->next == NULL) {
printf("无此结点\n");
return ERROR_INPUT;
}
}
// 删除第k个结点
q = p->next;
p->next = q->next;
// 释放指针q指向的内存空间
free(q);
q = NULL;
//更新结点总数
N--;
}
4. 存取操作
// 将链表中的第k个结点的数据域的值赋给item
int find(int k, struct Node *head)
{
struct Node *p;
int item;
int i;
i = 1;
// k合法?
if (k < 1) {
printf("存取位置不合法\n");
return ERROR_INPUT;
}
//找第k个结点
p = head;
while (p != NULL && i < k) {
p = p->next;
i++;
if (p == NULL) {
printf("无此结点\n");
return ERROR_INPUT;
}
}
// 存值并返回
item = p->data;
return item;
}
5. 查找操作
// 在链表中查找数据域为item的结点并返回其在表中的位置
int search(int item, struct Node *head)
{
struct Node *p;
int i;
p = head;
i = 1;
// 逐点访问
while (p != NULL && p->data != item) {
p = p->next;
i++;
}
if (p != NULL) {
return i;
} else {
printf("无此结点\n");
return ERROR_INPUT;
}
6. 遍历输出单链表的所有结点数据域
// 遍历所有结点并输出其数据域的值
void print(struct Node *head)
{
struct Node *p;
printf("Total number of nodes is:%d\n", N);
p = head;
if (head != NULL) {
do {
printf("%d ", p->data);
p = p->next;
} while (p != NULL);
}
printf("\n");
}
三、总结
下图是上述代码的运行结果,编译环境为linux,但在dev中也可正常运行,在Visual Studio中调整一下也可运行。图中的输入输出由前面的概括中的主函数决定。
第1行为提示初始单链表
第2行为初始化单链表
第3,4行为遍历输出当前的单链表(在第1个结点后插入22)
第5,6行为遍历输出当前的单链表(删除第三个结点)
第7行为存取链表中第二个结点,并将其值输出
第8行为在链表中查找数据域为3的结点并返回其在表中的位置
最后一行也为遍历输出当前的单链表
最后,很高兴你看到了这里,并且对上述代码进行了自己的研究,有了自己的收获。对了,还有不要忘记点赞噢。附上我喜欢的一句话,与君共勉。
走过的路,看过的风景,都是我们的底蕴,或许多学一点,对同一件事的看法就会不一样。