目录
虽然学习的时候能够看懂,但是还是应该复习的时候不看注释看懂每一行代码的含义和实现单链表的几个函数,这样才能记住这些复杂的东西.
链表介绍
struct Node
{
int date; 数据域
struct Node* next; 指针域
}; 以这个样子的struct为例
动态创建一个链表:动态内存申请+模块化设计(把以下功能设计成函数)
1.创建链表
2.创建结点
3.插入结点
4.删除结点
5.打印链表
链表是相对于可变数组更具有优势的一种动态内存实现,它的想法其实很简单,就是用了结构体和结构指针变量,实现了从把从表头开始到表尾之间的结点连接起来,表尾的指针最后应该指向NULL,用于区分.用火车来比喻是十分恰当的.
讲述链表的时候会用一些名词,笔者刚开始学习的也被繁多的名词劝退.
表头:本质是结构体变量,作为一个链表的开始,表头的数据是没有用的,相当于车头
结点:也就是结构体变量,只是区分表头表尾而已,相当于车厢
表尾(链末,链尾):本质是结构体变量,作为一个链表的结尾,它最后指向的是NULL,相当于车尾
指针域:一个链表用于连接各个结点的东西,它存在于一个结点中.相当于车钩
数据域:一个链表中各个结点用于存储数据的,比如姓名,分数等.相当于车厢中存储的东西
代码实现:
1.struct Node* createlist() 创建链表实质就是创建一个表头所以这里返回值是headNode
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
headNode->date = 1; 初始化,可以选择不初始化表头的date
headNode->next = NULL; 为了安全初始化为NULL
return headNode; 返回表头地址
}
2.struct Node* createNode(int date) 插入结点之前,需要先创建一个结点,参数为该个结点的数据
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->date = date; 传入新结点的date
newNode->next = NULL; 同理,为了安全初始化为NULL
return newNode; 返回新结点的地址
}
下面是插入结点的函数,但是实际上,插入的方式分为头部插入,尾部插入和指定位置插入,这里是头部插入的方式
3.void insertNodeByHead(struct Node* headNode, int date)
{
struct Node* newNode = createNode(date); 插入之前需要创建新的
newNode->next = headNode->next; 把新结点与下一个结点连起来
headNode->next = newNode; 把新结点和上一个结点连起来
相当于从A->C中间插入一个B,变成A->B->C
}
4.void deletNodeByAppoin(struct Node* headNode, int posData) 通过查找数据来找到删除结点
{
struct Node* posNode = headNode->next; 用于检测数据的结点,从表头的下一个结点开始
struct Node* posNodeFront = headNode; 表头和表头下一个结点开始扫描
if (posNode == NULL) 如果用于检测的结点是空,说明链表是空的
printf("链表是空的,无法删除\n");
else
{
while (posNode->date!=posData) 没有找到该数据的结点之前一直扫描下去
{
posNodeFront = posNode;
posNode = posNode->next;
if (posNode == NULL) 这里表示已经扫描完了,未找到该数据的结点
{
printf("已到达链表末尾,无法删除\n");
break;
}
}
posNodeFront->next = posNode->next; 这一行代码是为了连接删掉后的两个结点
free(posNode); 删除结点
}
}
5.void printList(struct Node* headNode)
{
struct Node* pMove = headNode->next; 需要一个指针来输出数据表头的数据不打印
while (pMove != NULL) 直到链末一直printf
{
printf("%d\t", pMove->date);
pMove = pMove->next; 这行代码很重要,从上一个结点传到下一个结点
}
printf("\n");
}
个人感想:
我觉得pMove=pMove->next特别能体系链表的优势,while循环内的这行代码,其实是不断的在往前"移动",以····-A-B-C-D-··· 这一部分链表为例子,pMove本身是一个指针变量,假设它初始指向A,那么pMove->next其实是B结点的地址,赋给pMove就把pMove这个指针往前移动了,个人认为这是十分巧妙的实现了结点之间的移动.