链表的概念及结构
概念:基于数组的缺点,数据结构中诞生了新的结构就是 链表
当一组数据需要频繁的进行插入、删除操作时候,链表则是首选的数据结构
链表也属于 线性表
链表的组成
链表由结点组成
结点分为两部分:
数据域,保存存储的数据元素
指针域,指向下一个结点的地址
注:链表也属于线性表,拥有线性表的所有特点
结点的创建:
struct Node{
int data; //数据域
struct Node *next; //指针域
};
结点结构体示意图:
实现单向链表,能够存放整型数据,设计需求如下:
设计需求:
链表的初始化
获取链表长度
插入指定位置结点
遍历链表
删除指定位置结点
清空链表
释放链表
链表的初始化
链表初始化 核心代码:
typedef struct node
{
int data;//数据域
struct node* next; //指针域
}Node, *LinkList; //Node是struct node的别名, LinkList 代表struct node*数据类型
//链表的初始化
LinkList creat_list()
{
LinkList head = (Node *)malloc(sizeof(Node));
if (!head)
{
printf("内存分配失败\n");
return NULL;
}
head->next = NULL; //带头结点的链表,只维护指针域,无需维护数据域
return head;
}
功能:
创建一个链表的头结点,并返回给使用者
链表长度获取
链表统计 核心代码:
//统计链表长度
int size_list(LinkList head)
{
if (head == NULL)
{
printf("传入链表为空\n");
return 0;
}
Node * pCur = head->next; //获取第一个有数据的结点
int num = 0;
while (pCur != NULL)
{
num++;
pCur = pCur->next;
}
return num;
}
功能:
统计链表中结点的个数并返回
链表的插入
链表插入 核心代码:
//插入结点 参数1 链表的头结点 参数2 插入位置 参数3 插入数据
void insert_list(LinkList head, int pos , int val)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
if (pos < 0 || pos > size_list(head))
{
printf("插入位置无效\n");
return;
}
//辅助指针,帮助我们找到待插入位置的前驱结点
Node* pCur = head;
for (int i = 0; i < pos; i++)
{
pCur = pCur->next;
}
//创建新结点
Node* newNode = (Node *)malloc(sizeof( Node));
if (!newNode)
{
return;
}
newNode->data = val;
newNode->next = NULL;
//将新结点插入到链表长
newNode->next = pCur->next;
pCur->next = newNode;
}
功能:
向指定的位置上,插入新的结点
链表的遍历
链表遍历 核心代码:
//遍历链表
void foreach_list(LinkList head)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
Node* pCur = head->next; //拿到第一个有数据的结点
while (pCur != NULL)
{
printf("%d ", pCur->data);
pCur = pCur->next;
}
printf("\n");
}
功能:
打印链表中每个结点的数据域
链表的删除
链表删除 核心代码:
//删除结点 参数1 链表表头 参数2 删除位置
void delete_list(LinkList head, int pos)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
if (pos < 0 || pos > size_list(head) - 1)
{
printf("删除位置无效\n");
return;
}
//辅助指针,帮助我们找到待删除位置的前驱结点
Node* pCur = head;
for (int i = 0; i < pos; i++)
{
pCur = pCur->next;
}
//记录待删除结点
Node* pDel = pCur->next;
//更改指针,删除结点
pCur->next = pDel->next;
//释放待删除的结点
free(pDel);
}
功能:
删除指定位置上的结点
链表的清空
链表清空 核心代码:
//清空链表
void clear_list(LinkList head)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
//指向第一个有数据的结点
Node * pCur = head->next;
while (pCur != NULL)
{
//先保存住下一个节点的位置
Node * pNext = pCur->next;
//释放节点
free(pCur);
pCur = pNext;
}
head->next = NULL;
}
功能:
删除指定位置上的结点
链表的销毁
链表清空 核心代码:
//销毁链表
void destroy_list(LinkList head)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
//先清空链表
clear_list(head);
//将头结点也释放掉,完成整个链表的销毁
free(head);
head = NULL;
}
功能:
删除指定位置上的结点
完整代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct node
{
int data;//数据域
struct node* next; //指针域
}Node, *LinkList; //Node是struct node的别名, LinkList 代表struct node*数据类型
//链表的初始化
LinkList creat_list()
{
LinkList head = (Node *)malloc(sizeof(Node));
if (!head)
{
printf("内存分配失败\n");
return NULL;
}
head->next = NULL; //带头结点的链表,只维护指针域,无需维护数据域
return head;
}
//统计链表长度
int size_list(LinkList head)
{
if (head == NULL)
{
printf("传入链表为空\n");
return 0;
}
Node * pCur = head->next; //获取第一个有数据的结点
int num = 0;
while (pCur != NULL)
{
num++;
pCur = pCur->next;
}
return num;
}
//插入结点 参数1 链表的头结点 参数2 插入位置 参数3 插入数据
void insert_list(LinkList head, int pos , int val)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
if (pos < 0 || pos > size_list(head))
{
printf("插入位置无效\n");
return;
}
//辅助指针,帮助我们找到待插入位置的前驱结点
Node* pCur = head;
for (int i = 0; i < pos; i++)
{
pCur = pCur->next;
}
//创建新结点
Node* newNode = (Node *)malloc(sizeof( Node));
if (!newNode)
{
return;
}
newNode->data = val;
newNode->next = NULL;
//将新结点插入到链表长
newNode->next = pCur->next;
pCur->next = newNode;
}
//遍历链表
void foreach_list(LinkList head)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
Node* pCur = head->next; //拿到第一个有数据的结点
while (pCur != NULL)
{
printf("%d ", pCur->data);
pCur = pCur->next;
}
printf("\n");
}
//删除结点 参数1 链表表头 参数2 删除位置
void delete_list(LinkList head, int pos)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
if (pos < 0 || pos > size_list(head) - 1)
{
printf("删除位置无效\n");
return;
}
//辅助指针,帮助我们找到待删除位置的前驱结点
Node* pCur = head;
for (int i = 0; i < pos; i++)
{
pCur = pCur->next;
}
//记录待删除结点
Node* pDel = pCur->next;
//更改指针,删除结点
pCur->next = pDel->next;
//释放待删除的结点
free(pDel);
}
//清空链表
void clear_list(LinkList head)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
//指向第一个有数据的结点
Node * pCur = head->next;
while (pCur != NULL)
{
//先保存住下一个节点的位置
Node * pNext = pCur->next;
//释放节点
free(pCur);
pCur = pNext;
}
head->next = NULL;
}
//销毁链表
void destroy_list(LinkList head)
{
if (head == NULL)
{
printf("传入链表为空\n");
return;
}
//先清空链表
clear_list(head);
//将头结点也释放掉,完成整个链表的销毁
free(head);
head = NULL;
}
int main() {
LinkList head = creat_list(); //创建链表,获取表头
//打印链表长度
printf("链表长度为:%d\n", size_list(head));
//插入结点
insert_list(head, 0, 10);
insert_list(head, 0, 20);
insert_list(head, 1, 30);
insert_list(head, 0, 40);
//遍历链表
printf("插入结点后,遍历结果为:\n"); // 40 20 30 10
foreach_list(head);
//打印链表长度
printf("链表长度为:%d\n", size_list(head));
//删除结点
delete_list(head, 2);
printf("删除结点后,遍历结果为:\n"); // 40 20 10
foreach_list(head);
//清空链表
clear_list(head);
printf("清空链表后,遍历结果为:\n");
foreach_list(head);
//销毁链表
destroy_list(head);
system("pause");
return EXIT_SUCCESS;
}