链式存储
优点:不要求大片连续空间,改变容量方便
缺点:不可随机存取,要耗费一定空间存放指针
两部分组成(数据域 + 指针域)
逻辑上相邻的数据元素在物理上不一定相邻
访问只能通过头指针进入链表 并通过每个元素指针域顺序查找其他元素节点(顺序存取法)
综上:该存储方式适合频繁插入删除 场景,不适合频繁查找访问数据
代码实现(C语言)
定义链表
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点数据类型
typedef int DataType;
// 定义链表节点结构
typedef struct Node
{
DataType data;
struct Node *next;
} LinkNode, *LinkList; //这两个都是结构体的别名,
//LinkNode *p 代表定义一个指针类型的节点p(强调节点),也可以表示为 LinkList p;(强调是一个单链表),为了可读性
初始化
LinkList InitList()
{
LinkList L;
L = (LinkNode *)malloc(sizeof(LinkNode)); //内存分配节点 c++ 是 L=new LinkNode;
if (L == NULL)
{
printf("内存分配失败");
exit(1);
}
L->next = NULL;
return L;
}
使用头插法建立单链表
时间复杂度O(n)
头插法是逆序插入,在头节点后,次新节点前插入新节点
void CreateList_H(LinkList L, int n) //n代表节点数
{
int i; //当前节点序号
LinkNode *s;
for (i = 0; i < n; i++) //循环建立、插入 n个节点
{
s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL)
{
printf("内存分配失败");
exit(1);
}
printf("请输入第 %d 个元素值: ", i + 1); //节点序号是从0开始的
scanf("%d", &s->data); //输入的值传给节点的data域
s->next = L->next; //节点插入在头指针的下一个,也就是其他节点之前
L->next = s; //连接起来
}
}
尾插法建立单链表
时间复杂度O(n)
顺序插入,定义一个尾指针,循环在尾指针的下一个插入新节点,最后再将最后一个节点的next域设空。
void CreateList_R(LinkList L, int n)
{
LinkNode *s, *r; //s是新节点的指针,r是尾指针
int i;
r = L;
for (i = 0; i < n; i++) //循环建立、插入 n个节点
{
s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL)
{
printf("内存分配失败");
exit(1);
}
printf("请输入第 %d 个元素值: ", i + 1);
scanf("%d", &s->data);
r->next = s; //在最后一个节点后面插入新节点s
r = s; //尾指针r移动到 新插入的节点s处
}
r->next = NULL; //循环结束后最后一个节点的next域指向空
}
ps:如果不懂头插法和尾插法如何实现的可以看下这个博主画的动图,很生动形象:
链接: 单链表之头插法和尾插法(动图+图解)
单链表判空
int IsEmpty(LinkList L)
{
return L->next ==NULL; //若为真返回1 表示空表,为假返回0
}
获取表长
定义一个计数器,遍历一遍链表,计数器++;
时间复杂度O(n)
int GetLength(LinkList L)
{
int length = 0;
LinkNode *p = L->next; //定义一个指针指向第一个节点
while(p) //与while(p!= NULL)等价
{
length++;
p = p->next;
}
return length; //返回表长
}
按值查找
循环链表,从第一个元素向后一个个进行值的比对
时间复杂度O(n)
int SearchByValue(LinkList L, DataType key)
{
int index = 1;
LinkNode *p = L->next;
while (p) //与while(p!= NULL)等价
{
if (p->data == key)
{
return index;
}
p = p->next;
index++;
}
return 0; //如果没找到与key相匹配的元素 返回0
}
按位查找
时间复杂度O(n)
建一个计数器,遍历链表,计数器++直到和位置相等。最后返回查找元素的值
DataType SearchByPosition(LinkList L, int i)
{
LinkNode *p = L->next;
int j = 1;
while (p != NULL && j < i)
{
p = p->next;
j++;
}
if (p == NULL || j > i)
{
printf("未找到第 %d 个节点,可能链表为空或位置不正确", i);
exit(1);
}
return p->data;
}
插入元素
第一步:先找到所查位置的前一个节点
第二步: 元素分配节点并赋值
第三步: 将节点插入进去,先插“尾巴” 后连“头”.
插入操作时间复杂度O(1),查找O(n)
int InsertElem(LinkList L, int i, DataType key)
{
LinkNode *p, *s;
int j = 0;
p = L; //新节点可以插入在第一个节点之前
while (p && j < i - 1) //找到需要插入位置的前一个节点
{
p = p->next;
j++;
}
if (!p || j > i - 1) //!p 和 p ==NULL 等价
{
printf("未找到第 %d 个节点,可能链表为空或位置不正确", i - 1);
return 0;
}
s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL)
{
printf("内存分配失败,无法插入新节点");
return 0;
}
s->data = key; //新节点的数据域 赋值
s->next = p->next; //插入先插“尾巴”
p->next = s; //后插“头”
return 1;
}
删除元素
第一步:先找到所查位置的前一个节点
第二步: 断开连接
第三步:将要删节点的值取出来,释放节点内存
删除操作时间复杂度O(1),查找O(n)
int DelElem(LinkList L, int i)
{
LinkNode *p, *q;
int j = 0;
DataType x; //要删除元素的值
p = L;
while (p->next && j < i - 1) //删除不能删最后一个的下一个,增加可以增加到最后一个的下一个
{
p = p->next;
++j;
}
if (!(p->next) || j > i - 1)
{
return 0;
}
q = p->next; //把要删除的节点“拎”出来 给q
p->next = q->next; //要删除的节点断开连接
x = q->data; //将删除节点的值 赋给x
free(q); //释放节点内存
return 1;
}
清空链表
时间复杂度O(n)
有一个中间指针用来存储下一个节点的地址,循环赋值 释放;
void ClearList(LinkList L)
{
LinkNode *p=L->next;
LinkNode *q;
while(p)
{
q=p->next;
free(p);
p=q;
}
L->next =NULL;
}
ps:清空和销毁的区别是,清空之后是个表,还有头节点,只不过头节点指向空,销毁之后就没表了也没头节点.
销毁链表
时间复杂度O(n)
void DestroyList(LinkList L)
{
LinkNode *p=L;
while(p)
{
L=L->next;
free(p);
p=L;
}
}
打印链表
时间复杂度O(n)
void DispList(LinkList L)
{
LinkNode *p = L->next;
while (p != NULL)
{
printf("%5d", p->data); //输出链表中的值
p = p->next;
}
}
完整示例(C语言实现)
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点数据类型
typedef int DataType;
// 定义链表节点结构
typedef struct Node
{
DataType data;
struct Node *next;
} LinkNode, *LinkList;
// 初始化链表
LinkList InitList()
{
LinkList L;
L = (LinkNode *)malloc(sizeof(LinkNode));
if (L == NULL)
{
printf("内存分配失败");
exit(1);
}
L->next = NULL;
return L;
}
// 使用头插法创建链表
void CreateList_H(LinkList L, int n)
{
int i;
LinkNode *s;
for (i = 0; i < n; i++)
{
s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL)
{
printf("内存分配失败");
exit(1);
}
printf("请输入第 %d 个元素值: ", i + 1);
scanf("%d", &s->data);
s->next = L->next;
L->next = s;
}
}
// 使用尾插法创建链表
void CreateList_R(LinkList L, int n)
{
LinkNode *s, *r;
int i;
DataType x;
r = L;
for (i = 0; i < n; i++)
{
s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL)
{
printf("内存分配失败");
exit(1);
}
printf("请输入第 %d 个元素值: ", i + 1);
scanf("%d", &x);
s->data = x;
r->next = s;
r = s;
}
r->next = NULL;
}
// 检查链表是否为空
int IsEmpty(LinkList L)
{
return L->next == NULL;
}
// 获取链表长度
int GetLength(LinkList L)
{
int length = 0;
LinkNode *p = L->next;
while (p != NULL)
{
length++;
p = p->next;
}
return length;
}
// 按值查找元素
int SearchByValue(LinkList L, DataType key)
{
int index = 1;
LinkNode *p = L->next;
while (p != NULL)
{
if (p->data == key)
{
return index;
}
p = p->next;
index++;
}
return 0;
}
// 按位置查找元素
DataType SearchByPosition(LinkList L, int i)
{
LinkNode *p = L->next;
int j = 1;
while (p != NULL && j < i)
{
p = p->next;
j++;
}
if (p == NULL || j > i)
{
printf("未找到第 %d 个节点,可能链表为空或位置不正确", i);
exit(1);
}
return p->data;
}
// 插入元素
int InsertElem(LinkList L, int i, DataType key)
{
LinkNode *p, *s;
int j = 0;
p = L;
while (p && j < i - 1)
{
p = p->next;
j++;
}
if (!p || j > i - 1)
{
printf("未找到第 %d 个节点,可能链表为空或位置不正确", i - 1);
return 0;
}
s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL)
{
printf("内存分配失败,无法插入新节点");
return 0;
}
s->data = key;
s->next = p->next;
p->next = s;
return 1;
}
// 删除元素
int DelElem(LinkList L, int i)
{
LinkNode *p, *q;
int j = 0;
DataType x;
p = L;
while (p->next && j < i - 1)
{
p = p->next;
++j;
}
if (!(p->next) || j > i - 1)
{
return 0;
}
q = p->next;
p->next = q->next;
x = q->data;
free(q);
return 1;
}
//清空链表
void ClearList(LinkList L)
{
LinkNode *p=L->next;
LinkNode *q;
while(p)
{
q=p->next;
free(p);
p=q;
}
L->next =NULL;
}
//销毁链表
void DestoryList(LinkList L)
{
LinkNode *p=L;
while(p)
{
L=L->next;
free(p);
p=L;
}
}
// 打印链表
void DispList(LinkList L)
{
LinkNode *p = L->next;
while (p != NULL)
{
printf("%5d", p->data);
p = p->next;
}
}
// 菜单函数
void MenuLine()
{
printf("\n 线性表子系统");
printf("\n =================================================");
printf("\n| 1 - 初始化链表 |");
printf("\n| 2 - 头插法创建链表 |");
printf("\n| 3 - 尾插法创建链表 |");
printf("\n| 4 - 检查链表是否为空 |");
printf("\n| 5 - 获取链表长度 |");
printf("\n| 6 - 按值查找元素 |");
printf("\n| 7 - 按位置查找元素 |");
printf("\n| 8 - 插入元素 |");
printf("\n| 9 - 删除元素 |");
printf("\n| 10 -清空元素 |");
printf("\n| 11 -销毁列表 |");
printf("\n| 12- 打印列表 |");
printf("\n| 0 - 返回 |");
printf("\n =================================================");
printf("\n请输入菜单编号 (0-9): ");
}
int main()
{
LinkList L = NULL;
DataType x;
int i, n;
char ch1, ch2;
ch1 = 'y';
while (ch1 == 'y' || ch1 == 'Y')
{
MenuLine();
scanf(" %c", &ch2);
getchar();
switch (ch2)
{
case '1':
L = InitList();
printf("链表已初始化");
break;
case '2':
printf("请输入要创建的线性表长度: ");
scanf("%d", &n);
CreateList_H(L, n);
printf("使用头插法创建的链表:\n");
DispList(L);
break;
case '3':
printf("请输入要创建的线性表长度: ");
scanf("%d", &n);
CreateList_R(L, n);
printf("使用尾插法创建的链表:\n");
DispList(L);
break;
case '4':
if (IsEmpty(L))
{
printf("链表为空");
}
else
{
printf("链表非空");
}
break;
case '5':
printf("链表长度为: %d", GetLength(L));
break;
case '6':
printf("请输入要查找的元素值: ");
scanf("%d", &x);
i = SearchByValue(L, x);
if (i == 0)
{
printf("未找到该元素");
}
else
{
printf("元素值 %d 在第 %d 个位置", x, i);
}
break;
case '7':
printf("请输入要查找的元素位置: ");
scanf("%d", &i);
x = SearchByPosition(L, i);
printf("第 %d 个元素值为: %d", i, x);
break;
case '8':
printf("请输入要插入的元素位置和值: ");
scanf("%d %d", &i, &x);
if (InsertElem(L, i, x))
{
printf("插入成功");
}
else
{
printf("插入失败");
}
break;
case '9':
printf("请输入要删除的元素位置: ");
scanf("%d", &i);
if (DelElem(L, i))
{
printf("删除成功");
}
else
{
printf("删除失败");
}
break;
case '10':
ClearList(L);
printf("链表已清空");
break;
case '11':
DestroyList(L);
printf("链表已销毁");
L = NULL; // 将链表指针置为NULL
break;
case '12':
printf("当前链表内容为: ");
DispList(L);
break;
case '0':
return 0;
default:
printf("无效菜单选项");
break;
}
}
return 0;
}
结语
敬请各位大佬批评指正 日后也会持续更新数据结构相关内容,互相学习😊😊
1512

被折叠的 条评论
为什么被折叠?



