目录
线性表
线性表是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串等。
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。
一、顺序表
1.1顺序表概念
顺序表是用一段物理地址连续的存储单元依次存储具有相同数据类型元素的线性结构,一般情况下采用数组存储,因此可以通过元素在表中的位置(通常称为索引或下标)直接访问和操作这些元素。在数组上完成数据的增删查改。
顺序表一般可以分为:
1. 静态顺序表:使用定长数组存储元素。
2. 动态顺序表:使用动态开辟的数组存储。
为了使灵活性提高,防止空间浪费,所以基本都是使用动态顺序表。
1.2顺序表相关操作
//初始化
//销毁
//打印
//扩容
//尾插
//尾删
//头插
//头删
//下标插入
//下标删除
//查找
//修改
//按照下标查找内容
下面我们一一实现。
1.3顺序表实现
我们先创建三个文件,一个头文件:SqList.h,两个源文件: SqList.c,test.c
头文件定义结构体和函数, SqList.c完成函数,test.c测试。更直观。
顺序表结构体创建:
typedef int SLDataType;
typedef struct SqList
{
SLDataType* a;
int size;
//当前元素个数
int capacity;
//动态空间大小
}SL;
在这个顺序表结构体中,需要定义一个指针a,指针a指向顺序表这个数组的起始地址,这个地址所存放的变量的数据类型是SLDataType,在这里定义为int类型,如果需要存放其它类型的数据,只需要改变语句中的int为想要存储的数据类型即可。还需要定义一个整型变量size,表示当前顺序表中元素个数,再定义一个整型变量capacity,表示当前动态空间的大小,便于数组空间不够时及时扩充容量。
我们先来初始化一个顺序表,用malloc函数先给数组动态开辟四个空间,每个空间的大小为我们需要存放的数据类型的大小,此时表中没有数据,令size为0,capacity为4。
void SLInit(SL* sq)
{
sq->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);
//验证是否开辟成功
if (sq->a == NULL)
{
perror(malloc);
exit(-1);
}
sq->size = 0;
sq->capacity = 4;
//先分配一些空间
}
销毁顺序表,释放头节点,令指针置空,size,从capacity为0即可,
void SLDestroy(SL* sq)
{
free(sq->a);
sq->a = NULL;
sq -> size = 0;
sq->capacity = 0;
}
为了方便验证,还需要一个打印函数,循环打印即可。
void SLPrint(SL* sq)
{
int i = 0;
for (i = 0; i < sq->size; i++)
{
printf("%d ", sq->a[i]);
}
printf("\n");
}
在动态空间容量不够时,需要扩充动态空间,这个函数很重要。判断当元素个数和空间大小相等时,扩充容量,使用realloc函数。
void SLExpand(SL* sq)
{
if (sq->size == sq->capacity)
{
SLDataType* tmp = (SLDataType*)realloc(sq->a, sq->capacity * 2 * sizeof(SLDataType));
if (tmp == NULL)
{
perror(realloc);
exit(-1);
}
sq->a = tmp;
sq->capacity *= 2;
}
}
接下来就是实现顺序表的增删查改。注意,每写一个函数都需要验证一下是否有问题,避免不知道哪里出错。
尾插:
首先判断是否需要扩容,在后面增元素都需要判断,然后直接插入即可,记得令size同步。
void SLPushBack(SL* sq, SLDataType x)
{
SLExpand(sq);
sq->a[sq->size] = x;
sq->size++;
}
尾删:
这里就是强硬的断言判断顺序表中是否还有元素能够删除,如果为空程序直接错误。
void SLPopBack(SL* sq)
{
assert(sq->size > 0);
sq->size--;
}
头插:
不能直接插入,需要一步一步将元素向后移一个,然后再头插入。
void SLPushFront(SL* sq, SLDataType x)
{
SLExpand(sq);
int end = sq->size - 1;
while (end >= 0)
{
sq->a[end + 1] = sq->a[end];
--end;
}
sq->a[0] = x;
sq->size++;
}
头删:
头插需要一步一步将元素后移,同样的头删也需要将元素一步一步往前移动。
void SLPopFront(SL* sq)
{
assert(sq);
assert(sq->size > 0);
int begin = 1;
while (begin < sq->size)
{
sq->a[begin - 1] = sq->a[begin];
++begin;
}
sq->size--;
}
按下标插入:
从后往前一个一个将元素向后移,直到空出下标位置,插入给定元素x。
void SLInsert(SL* sq, int pos, SLDataType x)
{
assert(sq);
assert(pos >= 0 && pos <= sq->size);
SLExpand(sq);
int end = sq->size - 1;
while (end >= pos)
{
sq->a[end + 1] = sq->a[end];
--end;
}
sq->a[pos] = x;
sq->size++;
}
按下标删除:
从下标位置,依次将元素向前移动,覆盖掉pos位置的元素,size--。
void SLErase(SL* sq, int pos)
{
assert(sq);
assert(pos >= 0 && pos < sq->size);
int begin = pos + 1;
while (begin < sq->size)
{
sq->a[begin - 1] = sq->a[begin];
++begin;
}
sq->size--;
}
修改:
根据下标直接修改即可,注意断言,pos位是否存在。
void SLModify(SL* sq, int pos, SLDataType x)
{
assert(sq);
assert(pos >= 0 && pos < sq->size);
sq->a[pos] = x;
}
根据元素位置查找:
遍历顺序表,找到符合内容的下标返回即可。
int SLFind(SL* sq, SLDataType x)
{
assert(sq);
for (int i = 0; i < sq->size; i++)
{
if (sq->a[i] == x)
{
return i;
}
}
return -1;
}
根据下标查找:
注意断言,pos位是否存在。根据下标直接返回即可。
SLDataType SL_find(SL* sq, int pos)
{
assert(pos >= 0 && pos <= sq->size);
return sq->a[pos];
}
全部代码:
SqList.h:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;
typedef struct SqList
{
SLDataType* a;
int size;
//当前元素个数
int capacity;
//动态空间大小
}SL;
//初始化
void SLInit(SL *sq);
//销毁
void SLDestroy(SL *sq);
//打印
void SLPrint(SL* sq);
//扩容
void SLExpand(SL* sq);
//尾插
void SLPushBack(SL* sq, SLDataType x);
//尾删
void SLPopBack(SL* sq);
//头插
void SLPushFront(SL* sq, SLDataType x);
//头删
void SLPopFront(SL* sq);
//插在pos位
void SLInsert(SL* sq, int pos, SLDataType x);
//删除pos位的元素
void SLErase(SL* sq, int pos);
//查找
int SLFind(SL* sq, SLDataType x);
//修改
void SLModify(SL* sq, int pos, SLDataType x);
//按照下标查找内容
SLDataType SL_find(SL* sq, int pos);
SqList.c:
#include"SqList.h"
void SLInit(SL* sq)
{
sq->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);
if (sq->a == NULL)
{
perror(malloc);
exit(-1);
}
sq->size = 0;
sq->capacity = 4;
//先分配一些空间
}
void SLDestroy(SL* sq)
{
free(sq->a);
sq->a = NULL;
sq -> size = 0;
sq->capacity = 0;
}
void SLPrint(SL* sq)
{
int i = 0;
for (i = 0; i < sq->size; i++)
{
printf("%d ", sq->a[i]);
}
printf("\n");
}
void SLExpand(SL* sq)
{
if (sq->size == sq->capacity)
{
SLDataType* tmp = (SLDataType*)realloc(sq->a, sq->capacity * 2 * sizeof(SLDataType));
if (tmp == NULL)
{
perror(realloc);
exit(-1);
}
sq->a = tmp;
sq->capacity *= 2;
}
}
void SLPushBack(SL* sq, SLDataType x)
{
SLExpand(sq);
sq->a[sq->size] = x;
sq->size++;
}
void SLPopBack(SL* sq)
{
//if (sq->size = 0)
//{
// return;
// //温柔的断言
//}
assert(sq->size > 0);
sq->size--;
}
void SLPushFront(SL* sq, SLDataType x)
{
SLExpand(sq);
int end = sq->size - 1;
while (end >= 0)
{
sq->a[end + 1] = sq->a[end];
--end;
}
sq->a[0] = x;
sq->size++;
}
void SLPopFront(SL* sq)
{
assert(sq);
assert(sq->size > 0);
int begin = 1;
while (begin < sq->size)
{
sq->a[begin - 1] = sq->a[begin];
++begin;
}
sq->size--;
}
void SLInsert(SL* sq, int pos, SLDataType x)
{
assert(sq);
assert(pos >= 0 && pos <= sq->size);
SLExpand(sq);
int end = sq->size - 1;
while (end >= pos)
{
sq->a[end + 1] = sq->a[end];
--end;
}
sq->a[pos] = x;
sq->size++;
}
void SLErase(SL* sq, int pos)
{
assert(sq);
assert(pos >= 0 && pos < sq->size);
int begin = pos + 1;
while (begin < sq->size)
{
sq->a[begin - 1] = sq->a[begin];
++begin;
}
sq->size--;
}
void SLModify(SL* sq, int pos, SLDataType x)
{
assert(sq);
assert(pos >= 0 && pos < sq->size);
sq->a[pos] = x;
}
int SLFind(SL* sq, SLDataType x)
{
assert(sq);
//int i = sq->size-1;
//while (i)
//{
// if(sq->a[i] == x)
// {
// return i;
// }
// i--;
//}
//return NULL;
for (int i = 0; i < sq->size; i++)
{
if (sq->a[i] == x)
{
return i;
}
}
return -1;
}
SLDataType SL_find(SL* sq, int pos)
{
assert(pos >= 0 && pos <= sq->size);
return sq->a[pos];
}
test.c:
#include"SqList.h"
void test()
{
SL sq;
SLInit(&sq);
//SLDestroy(&sq);
SLPushBack(&sq, 20);
SLPushFront(&sq, 10);
//SLPopFront(&sq);
SLInsert(&sq, 1, 15);
SLErase(&sq, 1);
SLModify(&sq, 1, 3);
SLPrint(&sq);
//返回下标
printf("%d", SL_find(&sq, 1));
printf("%d",SLFind(&sq, 3));
}
int main()
{
test();
return 0;
}
二、链表
2.1链表概念
链表,是若干数据元素组成的线性序列,将数据元素像链条一样连接在一起。存储在链表中的数据元素被称为节点(node),单向链表每个节点有一个存储元素的数据域和一个指向下一个节点的指针域,指针用来存储下一个节点的内存地址。
链表可分为单向、双向。
有头节点没有头节点。头节点不存放数据。
循环非循环链表 。
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。这种结构出现较多,我们以这个实现链表。
2.2链表相关操作
//创建一个节点
//打印链表
//头插
//头删
//尾插
//尾删
//查找
//在pos位置插入
//删除pos位置的节点
下面一一实现。
2.3链表实现
我们还是先创建三个文件,一个头文件:SqList.h,两个源文件: SqList.c,test.c
头文件定义结构体和函数, SqList.c完成函数,test.c测试。更直观。
链表结构体:
只需要一个节点存储数据元素的具体值,和一个指向下一个节点的指针。
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
创建一个节点:
也就是申请节点需要的动态空间,并初始化这个节点,存入数据,next指向空。
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
打印链表:
这里面cur实参,head形参,如果直接用head的话,会改变head的值,导致head指向的链表丢失,所以用cur来遍历链表 。
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
//while (cur != NULL)
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
尾插:
因为要改变的结构体的指针,所以要用二级指针 。
要先找到尾节点。
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
if (*pphead == NULL)
{
// 改变的结构体的指针,所以要用二级指针
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
// 改变的结构体,用结构体的指针即可
tail->next = newnode;
}
}
头插:
头插就简单些。
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
//创建一个新节点
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
//head指向新节点
*pphead = newnode;
}
尾删:
有:没有节点、一个节点或多个节点的区分。
void SLTPopBack(SLTNode** pphead)
{
// 1、空
assert(*pphead);
// 2、一个节点
// 3、一个以上节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
头删:
有空和非空两种可能,分开解决。
void SLTPopFront(SLTNode** pphead)
{
// 空
assert(*pphead);
// 非空
SLTNode* newhead = (*pphead)->next;
free(*pphead);
*pphead = newhead;
}
查找:
遍历查找即可。
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
pos位置插入:
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
删除pos位置的节点:
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
//pos = NULL;
}
}
全部代码:
SqList.h:
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
void SLTPrint(SLTNode* phead);
SLTNode* BuySListNode(SLTDataType x);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos);
SqList.c:
#include"SqList.h"
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
//while (cur != NULL)
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
if (*pphead == NULL)
{
// 改变的结构体的指针,所以要用二级指针
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
// 改变的结构体,用结构体的指针即可
tail->next = newnode;
}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead)
{
// 1、空
assert(*pphead);
// 2、一个节点
// 3、一个以上节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//SLTNode* tailPrev = NULL;
//SLTNode* tail = *pphead;
//while (tail->next)
//{
// tailPrev = tail;
// tail = tail->next;
//}
//free(tail);
tail = NULL;
//tailPrev->next = NULL;
SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SLTPopFront(SLTNode** pphead)
{
// 空
assert(*pphead);
// 非空
SLTNode* newhead = (*pphead)->next;
free(*pphead);
*pphead = newhead;
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
//pos = NULL;
}
}#include"SQlist.h"
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
//while (cur != NULL)
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
if (*pphead == NULL)
{
// 改变的结构体的指针,所以要用二级指针
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
// 改变的结构体,用结构体的指针即可
tail->next = newnode;
}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead)
{
// 1、空
assert(*pphead);
// 2、一个节点
// 3、一个以上节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//SLTNode* tailPrev = NULL;
//SLTNode* tail = *pphead;
//while (tail->next)
//{
// tailPrev = tail;
// tail = tail->next;
//}
//free(tail);
tail = NULL;
//tailPrev->next = NULL;
SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SLTPopFront(SLTNode** pphead)
{
// 空
assert(*pphead);
// 非空
SLTNode* newhead = (*pphead)->next;
free(*pphead);
*pphead = newhead;
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
//pos = NULL;
}
}
test.c:
#include"SqList.h"
void TestSList1()
{
int n;
printf("请输入链表的长度:");
scanf("%d", &n);
printf("\n请依次输入每个节点的值:");
SLTNode* plist = NULL;
for (size_t i = 0; i < n; i++)
{
int val;
scanf("%d", &val);
SLTNode* newnode = BuySListNode(val);
// 头插
newnode->next = plist;
plist = newnode;
}
SLTPrint(plist);
SLTPushBack(&plist, 10000);
SLTPrint(plist);
}
void TestSList2()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
SLTPrint(plist);
SLTPushFront(&plist, 10);
SLTPushFront(&plist, 20);
SLTPushFront(&plist, 30);
SLTPushFront(&plist, 40);
SLTPrint(plist);
}
void TestSList3()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
// SLTPopBack(&plist);
// SLTPrint(plist);
}
void TestSList4()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
SLTPopFront(&plist);
//SLTPopFront(&plist);
SLTPrint(plist);
}
int main()
{
TestSList4();
return 0;
}
三、顺序表和链表的比较
顺序表:
优点:
- 随机访问:顺序表中,元素在内存中是连续存储的,可以通过索引直接访问表中任意位置的元素,效率高。
- 内存利用率高:顺序表不像链表那样需要额外的指针来连接各个元素,因此在存储相同数量的元素时,顺序表所占用的内存空间会更小一些。
- 实现简单:基于数组的顺序表实现比较简单,通常只需要利用数组和一些基本的操作就可以完成。
缺点:
- 插入和删除操作效率低:在顺序表中,插入和删除元素的操作可能需要移动大量元素,特别是在表的中间或开头插入/删除元素时,这会导致较大的时间开销。
链表:
优点:
- 插入和删除操作高效:在链表中进行插入和删除操作比较方便,特别是在表的中间或开头插入/删除元素时,只需要修改相邻节点的指针即可,不需要像顺序表那样移动大量元素。
缺点:
- 随机访问效率低:链表中的元素并不是连续存储的,因此需要从头开始遍历链表直到找到目标元素,因此随机访问的效率比较低。
- 占用更多内存:相比于顺序表,链表需要额外的指针来连接各个节点,这样会占用更多的内存空间。
综合来看,顺序表适用于对随机访问需求较多的场景,但在频繁进行插入和删除操作的情况下,使用链表会更好。