逻辑结构:线性结构
存储结构:链式存储结构
特点:内存不连续,通过指针实现,大小不固定
1.概念
链表又称单链表、链式存储结构,用于存储逻辑关系为“一对一”的数据。
和顺序表不同同,使用链表存储数据,不强制要求数据在内存中集中存储,各个元素可以分散存储在内存中。例如:
需要通过指针实现:
所以在链表中,每个数据元素可以配有一个指针用于找到下一个元素即节点,这意味着,链表上的每个“元素”都长下图这个样子:
2.结构体定义
结构体:
struct node_t
{
int data ; //数据域
struct node _t * next ; //存放下一个节点的地址
}
3.有头和无头链表
3.1 无头单向链表
每个节点的数据域和指针域都有效
3.2 有头单向链表
存在一个头节点,头节点的数据域无效,但指针域有效
有头和无头链表的区别
- 有头链表:存在一个头节点,头节点数据域无效,指针域有效。
- 无头链表:每一个节点的数据域和指针域都有效。
首元结点位置:
- 不带头结点:首元结点就是第一个节点
- 带头节点:首元结点是头结点指向的下一个节点(也就是第二个节点),
判空条件:
- 不带头节点:head == NULL; 首元结点直接为空
- 带头节点:head->next == NULL; 头结点存在,但是首元结点为空
4.操作
3.1 遍历无头链表
定义一个结构体类型的指针指向首元节点,通过循环遍历打印数据域,每打印一个数据,指针就向后移动一位。
// 遍历无头链表
linknode_t *p = &A;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
3.2 遍历有头链表
定义一个指针指向头节点,然后跨过头结点,打印输出第二个节点的数据,每打印一个数据,指针就往后移一位
// 遍历有头链表
// 创建头节点
linknode_t head;
head.next = &A;
// 方法一:
// 定义指针指向头节点,
linknode_t *p = &head;
p = p->next; // 跨过头结点
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
// 方法二
// 先后移再打印
linknode_t *p = &head;
while (p->next != NULL)
{
p = p->next;
printf("%d ", p->data);
}
// 方法三
// 定义指针直接指向首元节点
linknode_t *p = head.next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
3.3 尾插法
写一个有头单向链表,用于保存输入的学生成绩,实现一输入学生成绩就创建一个新的节点,将成绩保存起来。再将该节点链接到链表的尾,直到输入-1结束。
要求:每个链表的节点由动态内存分配得到 , 也就是用malloc。
过程:
1. malloc申请结构体大小的空间作为头节点
2. 将新节点放到链表尾部
//(1) 新建节点用来保存数据
//(2) 初始化新节点,也就是保存数据, 指针域置空
//(3) 将新节点和老链表连接,利用尾指针
//(4) 将尾指针移动到新节点
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
} linknode_t, *linklist_p;
int main(int argc, char const *argv[])
{
// 开辟头结点
linknode_t *head = (linknode_t *)malloc(sizeof(linknode_t));
if (head == NULL)
{
perror("head malloc error");
return -1;
}
head->next = NULL;
int score;
// 开辟节点,作为尾节点,代替头结点进行移动
// 一开始,让尾指针指向头结点
linknode_t *ptail = head;
while (1)
{
scanf("%d", &score);
if (score == -1)
{
break;
}
else
{
// 新建节点,存入成绩
linknode_t *pcha = (linknode_t *)malloc(sizeof(linknode_t));
// 初始化新建节点,存入成绩
pcha->data = score;
pcha->next = NULL;
// 连接,尾指针指向新建节点
ptail->next = pcha;
// 移动尾节点,让插入的节点作为新的尾节点
ptail = pcha;
// ptail = ptail->next;
}
}
linknode_t *q = head;
while (q->next != NULL)
{
q = q->next;
printf("%d ", q->data);
}
printf("\n");
return 0;
}