线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的(地址不一定是连续的,但是可以通过之间存在的逻辑关系找到下一个数据)线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存 储。在数组上完成数据的增删查改。
静态顺序表指的是使用定长数组存储元素的顺序表,由于在使用之前,顺序表的个数就已经确定,对于需要大量增删查改的项目来说很鸡肋,所以静态顺序表只作为顺序表一个小的知识点一带而过。
//静态顺序表
typedef int SLDataType;
#define N 10
typedef struct Seqlist
{
SLDataType array[N];//定长数组
size_t size;//有效数据的个数
}SeqList;
动态顺序表指的是使用动态开辟的数组存储,顺序表的有效数据个数根据所需要进行修改。
//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* array; //指向动态开辟的数组
size_t size; //数据中存储的数据
size_t capacity; //数组的容量
}SeqList;
使用数据结构的目的就是为了对数据进行增删查改,那么对于动态顺序表的增删查改,我们使用接口的方式去详细的描述这一过程。
对于顺序表的初始化,主要就是针对顺序表的初始地址,有效数据个数以及最大容量进行设定,只有初始化成功才能开始正常的工作。
//顺序表初始化
void SeqListInit(SeqList* psl) //psl p指针,sl顺序表
{
assert(psl);
psl->array = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);
if (psl->array == NULL)
{
perror("fail in malloc");
return;
}
psl->size = 0;
psl->capacity = INIT_CAPACITY;
}
动态顺序表要时刻检查最大容量是否和有效数据个数相等,如果相等就说明要进行扩容操作
//检查空间,如果满了,进行增容
void SeqListCheckCapacity(SeqList* psl)
{
assert(psl);
if (psl->size == psl->capacity)
{
SLDataType* newnode = (SLDataType*)realloc(psl->array, sizeof(SLDataType)* 2 * psl->capacity);
if (newnode == NULL)
{
perror("fail in realloc");
return;
}
psl->array = newnode;
psl->capacity *= 2;
}
}
打印是一个很好检查自己代码错误以及输出的方法,一定要先写打印,然后用其测试头插头删,尾插尾删。
//打印顺序表
void PrintSL(SeqList* psl)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->array[i]);
}
printf("\n ");
}
从图中就可以明白,尾插其实就是赋值给array[size],前提是要检查一下存储空间够不够用
同时尾插之后要注意,数组存储的有效数据个数已经增加了
//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListCheckCapacity(psl);
psl->array[psl->size] = x;//size是顺序表的最后一个位置
psl->size++;
}
对于顺序表的删除,一定要注意,要保证有效数据个数,也就是size>0;如果没有数据你删什么。
从图中可以看出来,顺序表的尾删是什么,就是把尾巴的数据直接扔掉就行了。
//顺序表尾删
void SeqListPopBack(SeqList* psl)
{
assert(psl);
assert(psl->size > 0);
psl->size--;
}
很多小伙伴会质疑,realloc函数开辟出来的空间,不用的时候应该使用free函数将空间的使用权还给操作系统啊,但是请注意,动态内存函数开辟出来的空间,只能一起还给操作系统,不能一个一个free,我们可以在最后使用free函数将所有开辟出来的空间全部还给操作系统。
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListCheckCapacity(psl);
for (int i = psl->size; i > 0; i--)
{
psl->array[i] = psl->array[i - 1];
}
psl->array[0] = x;
psl->size++;
}
//顺序表头删
void SeqListPopFront(SeqList* psl)
{
assert(psl);
assert(psl->size > 0);
for (int i = 0; i < psl->size-1; i++)
{
psl->array[i] = psl->array[i + 1];
}
psl->size--;
}
int SeqListFind(SeqList* psl, SLDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
if (psl->array[i] == x)
{
return i;
}
}
return -1;
}
pos指的是数组的下标,在寻找的时候注意头和尾,pos=size 相当于尾插,
//顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
assert(pos>=0 && pos <= psl->size);
SeqListCheckCapacity(psl);
for (int i = psl->size; i > pos; i--)
{
psl->array[i] = psl->array[i - 1];
}
psl->array[pos] = x;
psl->size++;
}
上述代码可以规避pos=0的情况,如果pos=0;i可以在0的位置停下来。相当于是头插; 如果是这样的代码会出现什么情况呢
void SeqListInsert(SeqList* psl, size_t pos, SLDateType x)
{
assert(psl);
assert(pos<=psl->size);
for (int i = psl->size-1; i >= pos; i--)
{
psl->array[i + 1] = psl->array[i];
}
}
当在下标为0的位置上插入一个数据时,i从psl->size-1到0,i进行i--操作,此时i=-1,再执行i>=pos操作,此时会停止循环吗?答案是不会,大家可以去调试,确实不会让循环停下。
那为什么呢?因为pos为 size_t 的类型,size_t 为无符号整形-,当int(有符号整形)和size_t(无符号整形)比较大小时,int型的数据会发生算数转换,转换成unsigned int型,此时为负数的 i 就变成了很大的数字,自然而然比0大,因此会进入死循环。
删除pos位置的值其实就是另一种的头删,采用覆盖pos位置的值的方法
void SeqListErase(SeqList* psl, size_t pos)
{
assert(psl);
assert(pos >= 0 && pos < psl->size);
for (int i = pos; i < psl->size - 1; i++)
{
psl->array[i] = psl->array[i + 1];
}
psl->size--;
}
void SeqListDestory(SeqList* psl)
{
assert(psl);
free(psl->array);
psl->array = NULL;
psl->capacity = psl->size = 0;
}
#pragma once
#include
#include
#include
#define INIT_CAPACITY 4
typedef int SLDataType;
typedef struct Seqlist
{
SLDataType* array;
size_t size;
size_t capacity;
}SeqList;
//顺序表初始化
void SeqListInit(SeqList* psl); //psl p指针,sl顺序表
//检查空间,如果满了,进行增容
void SeqListCheckCapacity(SeqList* psl);
//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
//顺序表尾删
void SeqListPopBack(SeqList* psl);
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
//顺序表头删
void SeqListPopFront(SeqList* psl);
//顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);
//顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
//顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
//顺序表销毁
void SeqListDestory(SeqList* psl);
//打印顺序表
void PrintSL(SeqList* psl);
#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"
//打印顺序表
void PrintSL(SeqList* psl)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->array[i]);
}
printf("\n ");
}
//顺序表初始化
void SeqListInit(SeqList* psl) //psl p指针,sl顺序表
{
assert(psl);
psl->array = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);
if (psl->array == NULL)
{
perror("fail in malloc");
return;
}
psl->size = 0;
psl->capacity = INIT_CAPACITY;
}
//检查空间,如果满了,进行增容
void SeqListCheckCapacity(SeqList* psl)
{
assert(psl);
if (psl->size == psl->capacity)
{
SLDataType* newnode = (SLDataType*)realloc(psl->array, sizeof(SLDataType)* 2 * psl->capacity);
if (newnode == NULL)
{
perror("fail in realloc");
return;
}
psl->array = newnode;
psl->capacity *= 2;
}
}
//顺序表销毁
void SeqListDestory(SeqList* psl)
{
assert(psl);
free(psl->array);
psl->array = NULL;
psl->capacity = psl->size = 0;
}
//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListCheckCapacity(psl);
psl->array[psl->size] = x;//size是顺序表的最后一个位置
psl->size++;
}
//顺序表尾删
void SeqListPopBack(SeqList* psl)
{
assert(psl);
assert(psl->size > 0);
psl->size--;
//不能单独free,申请的空间要不然就一起释放掉,不能单独释放某一段空间
}
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListCheckCapacity(psl);
for (int i = psl->size; i > 0; i--)
{
psl->array[i] = psl->array[i - 1];
}
psl->array[0] = x;
psl->size++;
}
//顺序表头删
void SeqListPopFront(SeqList* psl)
{
assert(psl);
assert(psl->size > 0);
for (int i = 0; i < psl->size-1; i++)
{
psl->array[i] = psl->array[i + 1];
}
psl->size--;
}
//顺序表查找
int SeqListFind(SeqList* psl, SLDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
if (psl->array[i] == x)
{
return i;
}
}
return -1;
}
//顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
assert(pos>=0 && pos <= psl->size);
SeqListCheckCapacity(psl);
for (int i = psl->size; i > pos; i--)
{
psl->array[i] = psl->array[i - 1];
}
psl->array[pos] = x;
psl->size++;
}
//顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos)
{
assert(psl);
assert(pos >= 0 && pos < psl->size);
for (int i = pos; i < psl->size - 1; i++)
{
psl->array[i] = psl->array[i + 1];
}
psl->size--;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"
void Test()
{
SeqList seq;
SeqListInit(&seq);
SeqListPushBack(&seq, 1);
SeqListPushBack(&seq, 1);
SeqListPushBack(&seq, 1);
SeqListPushBack(&seq, 1);
SeqListPushBack(&seq, 1);
SeqListPushBack(&seq, 1);
SeqListPushBack(&seq, 1);
SeqListPushFront(&seq, 4);
SeqListPopFront(&seq);
SeqListInsert(&seq, 3, 6);
SeqListInsert(&seq, 3, 6);
SeqListInsert(&seq, 3, 6);
SeqListInsert(&seq, 3, 6);
SeqListInsert(&seq, 3, 6);
SeqListInsert(&seq, 3, 6);
SeqListErase(&seq, 3);
PrintSL(&seq);
SeqListDestory(&seq);
}
int main()
{
Test();
return 0;
}