文章目录
一、顺序表
1.顺序表的概念
通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。如图所示:
但需要注意的是:
1.顺序表的底层是数组,但它能提供许多现成的方法供程序员使用。
2.顺序表是线性表的一种(具有相同特性的数据结构集合)
但要区分的是:线性表的物理结构是不一定连续,逻辑结构是一定连续的。
而顺序表的物理结构和逻辑结构是一定连续的。
2.静态顺序表和动态顺序表
(1)静态顺序表-------静态开辟空间
#define N 100
struct seqlist
{
int arr[N];//无法改变N的大小,太大导致空间浪费,太小不够用
int size;//有效数据个数
};
(2)动态顺序表-------动态开辟空间
typedef int SLDataType;
typedef struct seqlist SL;
struct seqlist
{
SLDataType* arr;
int size;//有效数据个数
int capacity;//空间大小
};
动态顺序表的好处就是:空间不够我可以使用realloc函数进行增容,不会使空间太大导致浪费。
二、顺序表的基本实现
1.创建
2.初始化
为了让后续方便修改数据类型,我们用typedef定义一个新的数据类型,将它取名为SLDataType。其为了让使用结构体更方便,我们用typedef将它改名为SL。
#pragma once
#include<stdio.h>
#include<stdlib.h>
//定义顺序表结构和声明顺序表方法
//静态顺序表
//#define N 100
//
//struct seqlist
//{
// int arr[N];//无法改变N的大小,太大导致空间浪费,太小不够用
// int size;//有效数据个数
//};
//动态顺序表
typedef int SLDataType;
typedef struct seqlist SL;
struct seqlist
{
SLDataType* arr;
int size;//有效数据个数
int capacity;//空间大小
};
//顺序表初始化
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->size = 0;
ps->capacity = 0;
}
我们对代码进行调试:
可见没有任何问题,说明顺序表初始化成功
3销毁
因为内存是动态开辟而来的,所以当我们不需要使用时,切记要将内存返回给操作系统,否则会出现内存泄漏的风险。
//顺序表销毁
void SLDesTroy(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
4.扩容
在使用顺序表的过程中,我们难免会遇到空间不够的情况,这时就需要realloc函数对空间进行扩容处理。那问题是:我们需要申请多大的空间呢?
通常来说,增容都是成倍数增加的,一般增加2-3倍,为什么这么说呢?
如果频繁地增容,会造成程序执行的效率大大降低;而一下子给大了,又会导致空间浪费。
5.尾插
注意: 要对顺序表进行尾插是,先要看空间是否足够
//顺序表尾插
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
//插入数据之前先看空间够不够
if (ps->capacity == ps->size)
{
//申请空间
//malloc calloc realloc int arr[100] -->增容
//使用三目操作符判断当前空间大小
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * 2 * sizeof(SLDataType));//要申请多大空间
//判断空间是否申请成功
if (tmp == NULL)
{
perror("realloc fail");
exit(1);
}
//空间申请成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
ps->arr[ps->size++] = x;
}
6.尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
//顺序表不为空
--ps->size;
}
这里需要注意的是顺序表不能为空,如果顺序表为空,执行size–时会导致arr[size-1]=-1,数组下标不可能为负数。
7.头插
首先,还是判断插入数据是空间是否足够
头插的思路如图所示:
//顺序表头插
void SLPushFront(SL* ps, SLDataType x)
{
//判断空间是否足够
if (ps->size == ps->capacity)
{
//申请空间
//malloc calloc realloc int arr[100] -->增容
//使用三目操作符判断当前空间大小
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * 2 * sizeof(SLDataType));//要申请多大空间
//判断空间是否申请成功
if (tmp == NULL)
{
perror("realloc fail");
exit(1);
}
//空间申请成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
//使用for循环,让数据整体往后移动一位
for (int i = ps->size; i>0; i--)
{
ps->arr[i] = ps->arr[i - 1];//arr[1]=arr[0]
}
ps->arr[0] = x;
//增加完一个数据,要记得把size++,否则会导致最末端的数据消失
ps->size++;
}
8.头删
//头删
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
//数据整体往前挪到一位
for (int i = 0; i < ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
9.打印
当我们完成一个函数时,需要对其进行调试看是否有问题,如果想让它直接打印在屏幕上,则需调用打印这一函数方法即可
//顺序表打印
void SLPrint(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
10.在指定位置之前插入
//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
//插入数据判断空间够不够
if (ps->size == ps->capacity)
{
//申请空间
//malloc calloc realloc int arr[100] -->增容
//使用三目操作符判断当前空间大小
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * 2 * sizeof(SLDataType));//要申请多大空间
//判断空间是否申请成功
if (tmp == NULL)
{
perror("realloc fail");
exit(1);
}
//空间申请成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
//让pos及之后的数据整体往后挪动一位
for (int i = ps->size; i>pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
11.删除指定位置的数据
//删除指定位置数据
void SLErase(SL* ps, int pos)
{
assert(ps);
//pos不能<=ps->size,因为当pos=ps>size时,没有有效数据,无法删除
assert(pos >= 0 && pos < ps->size);
for (int i = pos; i<ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
有的人可能会有疑惑:为什么i不能等于ps->size-1呢?原因就是当i等于ps->size-1时,arr[size-1]=arr[size],而size没有数据,从而导致越界。
12.查找
//顺序表查找
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
//找到了
return i;
}
}
//没有找到
return -1;
}
13.修改
//顺序表的修改
void SLModify(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos && pos < ps->size);
ps->arr[pos] = x;
}
今天的分享就到这里了,希望能够帮到大家!感谢大家的阅读,我们下次再见(●’◡’●)