1.链表基本概念
链式存储的线性表,简称链表
链表其实是由一个或者多个结构体通过指针指向的关系构成
我们把每个结构体的变量称为节点,节点里面由两个成员组成
一个是数据域,另外一个是指针域,指针域是用于存放下一个节点的地址
以此类推,我们把这种存储方式称为链式存储
2.链表与顺序表差异
既然顺序存储中的数据因为挤在一起而导致需要成片移动,那很容易想到的解决方案是将数据离散地存储在不同内存块中,然后在用来指针将它们串起来。这种朴素的思路所形成的链式线性表,就是所谓的链表。
顺序表和链表在内存中基本状态如图所示:
3.链表的分类
根据链表中各个节点之间使用指针的个数,以及首尾节点是否相连,可以将链表细分为如下种类:
- 无头节点单向链表(LinkedList.c)
- 有头节点单向链表(LinkedListWithHead.c)
- 双向链表(BothwayLinkedList.c)
- 循环链表(LoopLinkedList.c)
这些不同的链表的操作都是差不多的,只是指针数目的异同。以最简单的单向链表为例,其基本示意图如下所示:
上图中,所有的节点均保存一个指针,指向其逻辑上相邻的下一个节点(末尾节点指向空)。另外注意到,整条链表用一个所谓的头指针 head 来指向,由 head 开始可以找到链表中的任意一个节点。head 通常被称为头指针。
链表的基本操作,一般包括:
- 节点设计
- 初始化空链表
- 增删节点
- 链表遍历
- 销毁链表
4.无头节点单链表节点设计
// 节点
struct node
{
int data; //数据域
// 指针域,指向下一个节点(存放下一个节点的地址)
struct node *next;
};
5.节点的空间分配
1.栈空间 : 过了生命周期自动释放
2.堆空间: malloc只需要保存地址即可,生命周期与程序保持一致
6.创建链表步骤
**1. 从无到有:**第一个节点的诞生,此时的首节点和尾节点都是它本身
2 .从少到多(添加节点过程):
1. 在原来链表的后面添加新节点(尾插法)
新节点接在尾节点后面,即尾节点指向新节点,原来的尾节点被新节点替代
特点 : 新接入的节点在前面,后接入的节点在后面
2.在原来的链表的前面添加新节点(头插法)
新节点接在原来链表的首节点前面,即新的节点指向原来的首节点,然后首节点被新节点替代
特点:后接入的节点在前面,先接入的节点在后面
7.学习链表方法:
一定要自己学会画图分析,再把思路转为代码,其实写代码要逻辑清晰才能写的快,切记你是怎么思考的就怎么写,没思考清除前别着急写。
demo:
1.创建新节点
// 创建新节点
struct node *create_newNode(dataType data)
{
struct node *pnew = malloc(sizeof(struct node));
if(pnew == NULL)
return NULL;
// 将新数据写入到新节点
pnew->data = data;
pnew->next = NULL;
return pnew;
}
2.创建新链表
// 创建新链表
struct node *create_list()
{
dataType data;
// 新节点指针,用于指向新节点
struct node *pnew = NULL;
// 指向首节点指针
struct node *head = NULL;
// 指向尾节点指针
struct node *tail = NULL;
while(1)
{
scanf("%d",&data);
if(data == 0)
break;
// 创建新节点
pnew = create_newNode(data);
if(pnew == NULL)
{
perror("create newNode failed:");
return NULL;
}
// 将新节点插入到链表
if(head == NULL)// 从无到有,此时首节点和尾节点都是pnew
{
head = pnew;
tail = pnew;
}
else // 从少到多
{
tail->next = pnew;
tail = pnew;// 更新尾节点
}
}
return head;
}
3.打印信息
// 打印信息
void showList(struct node *head)
{
for(struct node *p = head; p != NULL; p = p->next)
printf("%d ",p->data);
printf("\n");
}
练习:
1.实现头插法
2.实现链表的修改节点和删除节点
3.实现链表数据在任意位置添加
8.带头节点的单向链表
1.图解
// 定义数据节点
struct node
{
dataType data; // 数据域
struct node *next; // 指针域,存放(指向)下一个节点的地址
};
//-----------------------------------
// 定义头节点
struct headNode
{
struct node *first; // 指向第一个数据节点
struct node *last; // 指向最后一个数据节点
int nodeNumber; // 记录链表节点数
};
demo:
创建头节点
struct headNode* create_new_headNode()
{
struct headNode* head = malloc(sizeof(struct headNode));
if(head == NULL)
return NULL;
head->first = NULL;
head->last = NULL;
head->nodeNumber = 0;
return head;
}
创建数据节点
// 创建新节点
struct node* create_new_node(dataType data)
{
struct node* pnew = malloc(sizeof(struct node));
if(pnew == NULL)
return NULL;
pnew->data = data;
pnew->next = NULL;
return pnew;
}
尾插法
// 尾插法
void addTail(struct headNode *head,struct node* pnew)
{
head->last->next = pnew;
head->last = pnew;
}
创建链表
struct headNode* create_list()
{
// 创建头节点
struct headNode* head = create_new_headNode();
if(head == NULL)
return NULL;
dataType data = -1;
while(1)
{
scanf("%d",&data);
if(data == 0)
break;
// 创建新节点
struct node* pnew = create_new_node(data);
if(pnew == NULL)
return NULL;
// 从无到有
if(head->first == NULL)
{
head->first = pnew;
head->last = pnew;
}
else // 从少到多
{
// 尾插法
addTail(head,pnew);
}
// 更新节点
head->nodeNumber++;
}
return head;
}
显示链表
void showList(struct headNode* head)
{
if(head->first == NULL)
{
printf("链表为空\n");
return;
}
for(struct node* p = head->first;p != head->last->next;p = p->next)
{
printf("%d\t",p->data);
}
printf("\n");
printf("链表节点数为:%d\n",head->nodeNumber);
}