目录
首先,什么是顺序表呢?
顺序表是线性表的一种,那么线性表是什么呢?
线性表是n个具有相同特性的数据元素的有限序列,线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表,链表,栈,队列,字符串。
线性表在逻辑上是线性结构,也就是说是连续的一条直线。但是在物理结构上不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表要求是连续存储的。
话不多说,直接来写代码:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define N 10
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;
int size;
int capicity;
}SL;
//初始化
void SLInit(SL *ps);
//打印
void SLPrint(SL* ps);
//销毁
void SLDestory(SL* ps);
//尾插
void SLPushBack(SL* ps ,SLDateType x);
// 尾删
void SLPopBack(SL* ps);
//检查内存是否够、
void SLCheckCapicity(SL* ps);
//头插
void SLPushFront(SL* ps, SLDateType x);
//头删
void SLPopFront(SL* ps);
// 顺序表查找
int SLFind(SL* ps, SLDateType x);
// 顺序表在pos位置插入x
void SLInsert(SL* ps, size_t pos, SLDateType x);
// 顺序表删除pos位置的值
void SLErase(SL* ps, size_t pos);
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;
int size;
int capicity;
}SL;
这一段代码的意义是:定义一个顺序表的结构体采用动态存储,SLDateType是将int类型重定义,方便后续修改类型。
第一步,毋庸置疑是代码的初始化:
void SLInit(SL* ps)
{
ps->a = NULL;
ps->size = 0;
ps->capicity = 0;
}
由于是动态内存开辟,所以我们需要检查内存是否足够。
void SLCheckCapicity(SL* ps)
{
if (ps->size == ps->capicity)
{
int newcapicity = ps->capicity == 0 ? 4 : ps->capicity * 2;
SLDateType* ret = (SLDateType)realloc(ps->a, newcapicity * sizeof(SLDateType));
if (ret == NULL)
{
perror(realloc);
return;
}
ps->a = ret;
ps->capicity = newcapicity;
}
}
做完这些,该考虑如何增删查改了。
头插,头删
//头插
void SLPushFront(SL* ps, SLDateType x)
{
assert(ps);
//扩容
SLCheckCapicity(ps);
//第一种 for循环
//ps->a[ps->size] = x;
//int tt=0;
创建中间变量来交换数值
//int i = 0;
//for ( i = ps->size; i > 0; i--)
//{
// tt = ps->a[i - 1];
// ps->a[i - 1] = ps->a[i];
// ps->a[i] = tt;
//}
//ps->size++;
//第二种 SLInsert。
SLInsert(ps, 0, x);
}
//头删
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size > 0);
//第一种 for循环
for (int j = 0; j < ps->size-1; j++)
{
ps->a[j] = ps->a[j + 1];
}
ps->size--;
//pos位置删
SLErase(ps, 0);
}
这里可以使用while循环也可以用for循环看个人喜好。
尾插 尾删
//尾插
void SLPushBack(SL* ps,SLDateType x)
{
assert(ps);
//扩容
SLCheckCapicity(ps);
//第一种
ps->a[ps->size] = x;
ps->size++;
//第二种
SLInsert(ps, ps->size, x);
}
//尾删
void SLPopBack(SL* ps)
{
assert(ps->size>0);
ps->size--;
}
在特定位置插入,删除
// 顺序表在pos位置插入x
void SLInsert(SL* ps, size_t pos, SLDateType x)
{
//第一种
int j = 0;
for (j = ps->size-1; j >= pos; j--)
{
ps->a[j + 1] = ps->a[j];
}
ps->a[j] = x;
ps->size++;
//第二种
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
// 顺序表删除pos位置的值
void SLErase(SL* ps, size_t pos)
{
assert(ps);
assert(pos >= 0);
assert(pos <= ps->size);
int j = 0;
//第一种
for (j = pos-1; j < ps->size; j++)
{
ps->a[j] = ps->a[j + 1];
}
ps->size--;
//第二种
// 挪动数据覆盖
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
上面的头插,头删之类的也可以使用以上代码,是第二种方法,详细见上边的代码。
查找
//查找
int SLFind(SL* ps, SLDateType x)
{
assert(ps);
for (int i = 0; i < ps->size; ++i)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
由于本人比较懒,所以将测试代码写在了一起,宝子们凑合看看吧。
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void test()
{
SL s;
SLInit(&s);
SLPushBack(&s,1);
SLPushBack(&s, 4);
SLPushBack(&s, 1);
SLPushFront(&s, 5);
SLPushFront(&s, 5);
SLPushFront(&s, 5);
//头插尾插
//查找
SLFind(&s, 1);
SLFind(&s, 7);
//头删尾删
SLPopBack(&s);
SLPrint(&s);
SLPopFront(&s);
SLPrint(&s);
//在pos位置插入
SLInsert(&s,2,9);
SLPrint(&s);
SLErase(&s,2);
SLPrint(&s);
SLDestory(&s);
//删除数字
/*int pos = SLFind(&sl, 5);
if (pos != -1)
{
SLErase(&sl, pos);
}
SLPrint(&sl);*/
}
int main()
{
test();
return 0;
}
总代码:
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void SLInit(SL* ps)
{
ps->a = NULL;
ps->size = 0;
ps->capicity = 0;
}
void SLCheckCapicity(SL* ps)
{
if (ps->size == ps->capicity)
{
int newcapicity = ps->capicity == 0 ? 4 : ps->capicity * 2;
SLDateType* ret = (SLDateType)realloc(ps->a, newcapicity * sizeof(SLDateType));
if (ret == NULL)
{
perror(realloc);
return;
}
ps->a = ret;
ps->capicity = newcapicity;
}
}
void SLPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
void SLDestory(SL* ps)
{
//释放
if (ps->a)
{
free(ps->a);
ps->a = NULL;
ps->size = 0;
ps->capicity = 0;
}
}
//头插
void SLPushFront(SL* ps, SLDateType x)
{
assert(ps);
//扩容
SLCheckCapicity(ps);
//第一种 for循环
//ps->a[ps->size] = x;
//int tt=0;
创建中间变量来交换数值
//int i = 0;
//for ( i = ps->size; i > 0; i--)
//{
// tt = ps->a[i - 1];
// ps->a[i - 1] = ps->a[i];
// ps->a[i] = tt;
//}
//ps->size++;
//第二种 SLInsert。
SLInsert(ps, 0, x);
}
//尾插
void SLPushBack(SL* ps,SLDateType x)
{
assert(ps);
//扩容
SLCheckCapicity(ps);
//第一种
ps->a[ps->size] = x;
ps->size++;
//第二种
SLInsert(ps, ps->size, x);
}
//尾删
void SLPopBack(SL* ps)
{
assert(ps->size>0);
ps->size--;
}
//头删
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size > 0);
//第一种 for循环
for (int j = 0; j < ps->size-1; j++)
{
ps->a[j] = ps->a[j + 1];
}
ps->size--;
//pos位置删
SLErase(ps, 0);
}
//查找
int SLFind(SL* ps, SLDateType x)
{
assert(ps);
for (int i = 0; i < ps->size; ++i)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
// 顺序表在pos位置插入x
void SLInsert(SL* ps, size_t pos, SLDateType x)
{
//第一种
int j = 0;
for (j = ps->size-1; j >= pos; j--)
{
ps->a[j + 1] = ps->a[j];
}
ps->a[j] = x;
ps->size++;
//第二种
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
// 顺序表删除pos位置的值
void SLErase(SL* ps, size_t pos)
{
assert(ps);
assert(pos >= 0);
assert(pos <= ps->size);
int j = 0;
//第一种
for (j = pos-1; j < ps->size; j++)
{
ps->a[j] = ps->a[j + 1];
}
ps->size--;
//第二种
// 挪动数据覆盖
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
事物总是存在双面性,一个东西有好处肯定也会有不足,和人一样嘛!
顺序表的缺陷 :
1.空间不够,需要扩容,扩容(尤其是异地扩容是有一定代价的,还可能会有一定的空间浪费。
2.头部或中部插入删除,需要挪动数据,效率低下。
优化方案:按需申请释放,不要挪动数据。
这个涉及到我们的链表,下期来给大家分享,今天就到此为止吧。
感觉有收获的宝子们,可以点点赞和关注偶,有什么问题也可以私我