cpp
#include <stdio.h> // 引入标准输入输出库,用于输入输出操作
#include <stdlib.h> // 引入标准内存分配库,用于动态内存分配
#include <time.h> // 引入标准时间库,用于使用随机数生成函数
// 定义常量DL为3,未在后续代码中使用,可能为误提交或者待使用
#define DL 3
// 定义宏STR,将参数转换为对应的字符串表示
#define STR(n) #n
// 定义宏DIGIT_LEN_STR,将参数作为格式字符串与一个整数结合,生成对应的格式化字符串
#define DIGIT_LEN_STR(n) "%" STR(n) "d"
// 定义一个结构体Node,表示链表中的一个节点
typedef struct Node {
int data; // 节点的数据部分,此处为整型
struct Node *next; // 节点的下一个节点的指针
} Node;
// 定义一个函数getNewNode,用于创建一个新的节点并返回其指针
Node *getNewNode(int val) {
// 通过malloc动态分配内存,创建新节点
Node *p = (Node *)malloc(sizeof(Node));
// 设置新节点的数据部分
p->data = val;
// 设置新节点的下一个节点为NULL
p->next = NULL;
// 返回新节点的指针
return p;
}
// 定义一个函数insert,用于在链表的指定位置插入一个新的节点
Node *insert(Node *head, int pos, int val) {
// 创建一个新的头节点new_head,用于保存插入新节点后的链表头
Node new_head, *p = &new_head, *node = getNewNode(val);
// 将new_head的next指向原链表的头节点head
new_head.next = head;
// 在链表中找到指定位置的前一个节点,此处使用了一个for循环
for (int i = 0; i < pos; i++) p = p->next;
// 将新节点插入到指定位置,此处涉及链表的操作
node->next = p->next;
p->next = node;
// 返回新链表的头节点new_head的next指针,即插入新节点后的链表头
return new_head.next;
}
// 定义一个函数clear,用于清空链表中的所有节点,释放其内存空间
void clear(Node *head) {
// 如果链表为空,则直接返回,无需进行任何操作
if (head == NULL) return ;
// 遍历链表,逐个释放节点的内存空间,直至链表末尾
for (Node *p = head, *q; p; p = q) {
q = p->next; // q保存下一个节点,为下一次循环准备
free(p); // 释放当前节点的内存空间
}
return ; // 所有节点已经释放完毕,函数结束返回
}
// 输出链表的函数,参数为链表的头节点和标记flag,默认为0
void output_linklist(Node *head, int flag = 0) {
// 初始化节点计数为0
int n = 0;
// 遍历链表,计算链表的长度
for (Node *p = head; p; p = p->next) n += 1;
// 根据DL宏定义的长度,打印数字表示的索引和空格
for (int i = 0; i < n; i++) {
printf(DIGIT_LEN_STR(DL), i);
printf(" ");
}
// 打印换行符
printf("\n");
// 遍历链表,打印每个节点的值和箭头符号
for (Node *p = head; p; p = p->next) {
printf(DIGIT_LEN_STR(DL), p->data);
printf("->");
}
// 再打印换行符
printf("\n");
// 如果flag为0,打印额外的换行符
if (flag == 0) printf("\n\n");
// 函数结束并返回
return ;
}
// 在链表中查找值的函数,参数为链表的头节点和要查找的值
int find(Node *head, int val) {
// 初始化指针p为链表的头节点
Node *p = head;
// 初始化计数为0
int n = 0;
// 遍历链表,查找值为val的节点
while (p) {
// 如果找到值为val的节点
if (p->data == val) {
// 输出链表,标记为1,表示找到了值
output_linklist(head, 1);
// 计算打印的空格长度,为节点计数乘以每个节点的长度加上两个空格的长度再加两个空格的长度
int len = n * (DL + 2) + 2;
// 在打印的结果下方打印len个横线" -"
for (int i = 0; i < len; i++) printf(" ");
// 在打印的横线上方打印"^"符号
printf("^\n");
// 在打印的横线下方再打印len个横线" |"
for (int i = 0; i < len; i++) printf(" ");
// 在最下方打印"|"符号
printf("|\n");
// 返回1,表示找到了值为val的节点
return 1;
}
// 如果没找到,计数加1并移动到下一个节点
n += 1;
p = p->next;
}
// 如果遍历完链表都没找到值为val的节点,返回0,表示没找到
return 0;
}
// 程序的主函数开始
int main() {
// 使用当前时间作为随机数生成器的种子,这样每次运行程序时,生成的随机数都会不同
srand(time(0));
// 定义常量MAX_OP为7,这可能是一个操作的上限或者是要插入的节点数量
#define MAX_OP 7
// 创建一个指向Node类型的指针head,并将其初始化为NULL,这是链表的头节点
Node *head = NULL;
// 循环MAX_OP次,进行MAX_OP次操作
for (int i = 0; i < MAX_OP; i++) {
// 随机生成一个在0到i之间的位置,pos代表要插入新节点的位置
int pos = rand() % (i + 1);
// 随机生成一个0到99之间的值,val代表要插入的新节点的值
int val = rand() % 100;
// 打印出要进行的操作,即将val插入到pos位置
printf("insert %d at %d to linklist\n", val, pos);
// 调用insert函数,将新节点插入到链表的指定位置,并更新head为新的头节点
head = insert(head, pos, val);
// 调用output_linklist函数,打印出当前的链表状态
output_linklist(head);
}
// 从标准输入读取一个整数val,如果读取失败(输入的不是整数),则返回-1,所以~scanf("%d", &val)会变成-1的补码,是一个很大的数
int val;
while (~scanf("%d", &val)) {
// 调用find函数在链表中查找val,如果没找到,则返回0
if (!find(head, val)) {
// 如果没找到,打印"not found"
printf("not found\n");
}
}
// 调用clear函数,清除链表中的所有节点,释放其内存空间
clear(head);
// 返回0,表示程序正常结束
return 0;
}
这段代码是用C语言编写的,它定义了一个链表,并实现了一些基本的链表操作,如创建新节点、插入节点、清空链表、输出链表以及查找节点。以下是每段代码的详细解释:
-
文件注释:
- 这段代码是一个文件注释
-
包含头文件:
#include <stdio.h>
和#include <stdlib.h>
:这两个头文件包含了标准输入输出库和标准库,用于进行输入输出和内存分配等操作。#include <time.h>
:这个头文件包含了关于时间的函数,srand(time(0))
用于设置随机数生成器的种子。
-
宏定义:
#define DL 3
:这个宏定义了一个常量DL
,它的值为3。#define STR(n) #n
和#define DIGIT_LEN_STR(n) "%" STR(n) "d"
:这两个宏用于生成字符串表示的整型常量。例如,DIGIT_LEN_STR(3)
会被替换为"%3d"
。
-
结构体
Node
的定义:typedef struct Node { int data; struct Node *next; } Node;
:这定义了一个名为Node
的结构体类型,它有两个成员,一个data
用于存储节点数据,一个next
用于指向下一个节点。
-
函数
getNewNode
的定义:Node *getNewNode(int val) { Node *p = (Node *)malloc(sizeof(Node)); p->data = val; p->next = NULL; return p; }
:这个函数用于创建一个新的节点,它接收一个整型参数val
,创建一个新的Node
类型的对象,将val
赋值给新节点的data
成员,将新节点的next
设置为NULL
,并返回新创建的节点的指针。
-
函数
insert
的定义:Node *insert(Node *head, int pos, int val) { Node new_head, *p = &new_head, *node = getNewNode(val); new_head.next = head; for (int i = 0; i < pos; i++) p = p->next; node->next = p->next; p->next = node; return new_head.next; }
:这个函数用于在链表的指定位置插入一个新的节点,它接收三个参数,链表的头节点head
、要插入的位置pos
和要插入的值val
。它首先创建一个新的头节点new_head
,然后通过调用getNewNode
创建一个新的节点,将新节点的值设置为val
。然后通过循环找到要插入的位置,最后将新节点插入到链表的指定位置,并返回新的头节点的指针。
-
函数
clear
的定义:void clear(Node *head) { if (head == NULL) return ; for (Node *p = head, *q; p; p = q) { q = p->next; free(p); } return ; }
:这个函数用于清空链表,它接收一个参数,链表的头节点head
。它首先检查链表是否为空,如果是则直接返回。然后通过循环遍历链表,释放每个节点的内存空间。
-
函数
output_linklist
的定义:void output_linklist(Node *head, int flag = 0) { int n = 0; for (Node *p = head; p; p = p->next) n += 1; for (int i = 0; i < n; i++) { printf(DIGIT_LEN_STR(DL), i); printf(" "); } printf("\n"); for (Node *p = head; p; p = p->next) { printf(DIGIT_LEN_STR(DL), p->data); printf("->"); } printf("\n"); if (flag == 0) printf("\n\n"); return ; }
:这个函数用于输出链表,它接收两个参数,链表的头节点head
和一个标记flag
。它首先计算链表的长度,然后根据长度打印出相应数量的数字和空格,然后打印出链表中的所有元素和箭头符号。如果flag
为0,则在打印完链表