【数据结构】顺序表(上)
所属专栏:初始数据结构
博主首页:初阳785
代码托管:chuyang785
感谢大家的支持,您的点赞和关注是对我最大的支持!!!
博主也会更加的努力,创作出更优质的博文!!
关注我,关注我,关注我,重要的事情说三遍!!!!!!!!
1.线性表
1.1概念
概念:线性表(linear list)是n个具有相同特性的数据元素的有限序列。
线性表是一种实际中广泛使用的数据结构,常见的线性表:顺序表,链表,
栈,队列,字符串……
线性表在逻辑上是线性结构的,也就是说是连续的一条直线。但是在物理结构上
并不是连续的,线性表在物理上存储时,通常以数组和链式结构的形式储存。
顺序表的本质就是数组,但是不同于数组的时顺序表分动态的和静态的。
而且顺序表存放数据必须是连续的,数组没有要求。
看起来有点想数组。
2.顺序表
2.1顺序表的概念及结构
顺序表是用一段物理地址连续的存放单元,依次存储数据元素的线性结构,
一般情况先采用数组存储,在数组上完成数据的增删查改。
顺序表就是数组,但是在数组的基础上它还要求数据是从头开始连续存储的,不能跳跃间隔。
3.整体设计与接口函数的实现
所谓接口,就相当于已经设计好的程序,如果要用的话就是接调用这个接口。
你可以这样理解,我们在拧螺丝的时候可能需要不同的工具头,可能是平头的,也可能是交叉的,而我们如果想使用的话只需要把不同的工具头套在我们把手上使用即可,这些工具头就是我们的接口。
3.1初始化结构体
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;// 表示数组中存储了多少个数据
int capacity;//数组实际能存放数据的空间间容量是多大
}SL;
注意:这我们用了一个关键字typedef,typedef int SLDataType这里我我们将int进行重命名位
SLDataType,应为我们这是在实现接口,我们的接口要尽量的通用,这里我们要设计的数据类型可能不是int,肯能是double也可能是其他的类型的,所以如果我们要改的话只要在关键字那里改就行了,就会很方便。
3.2检查容量
void SeqListCheckCapacity(SL* ps)
{
//空间不足,扩容。
//SeqListCheckCapacity(ps);
if (ps->size == ps->capacity)//没空间或者空间满了
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//这里先给4给大小的容量
//ps->capacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
上面的三目操作发等同以下
//if (ps->capacity == 0)
//{
// ps->capacity = 4;
//}
//else
//{
// ps->capacity = ps->capacity * 2;
//}
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (tmp == NULL)
{
printf("relloc fial\n");
exit(-1);//退出函数
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
这里我们使用到了realloc动态内存开辟函数,realloc函数有个特点就是内存不够的时候它可以继续开辟内存,而且当我们我们还没有开辟内存的时候,第一次开辟内存等同于malloc。
3.3尾插
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
//printf("%d ", ps->a[ps->size]);
ps->size++;
}
这里最关键的是判断容量,如果满了我们就扩容。
3.4数据展示
void SeqListPrint(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
这个就没什么好说的,一个for循环就解决了。
3.5尾删
void SeqListPopBack(SL* ps)
{
//温柔的处理方式
//if (ps->size > 0)
//{
// ps->size--;
//}
//暴力的处理方式
assert(ps->size > 0);
ps->size--;
}
我们的查询数据的原理其实和数组是一样的,所以只要我们遍历不到我们想要的数据的下标就打印不出来,就等同于删除数据了。但是这里我们要注意的是如果我们的数据已经删完了,我们的ps->size就不能继续–下去了,不然就会得到负数,显然会出现错误。
3.6头插
void SeqListPushFront(SL* ps, SLDataType x)
{
//判断是否满了
SeqListCheckCapacity(ps);
int end = 0;
for (end = ps->size - 1; end >= 0; end--)
{
ps->a[end+1] = ps->a[end];
}
ps->a[0] = x;
ps->size++;
}
在进行头插的时候我们想到的办法就是平移法:
就是我们把现有的数据整体的网后平移一个位置,同时size++(还有就是别忘记判断容量)然后再在得一个位置插入我们的数据就行了。
3.7头删
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
int bigan = 1;
for (bigan = 1; bigan < ps->size; bigan++)
{
ps->a[bigan-1] = ps->a[bigan];
}
ps->size--;
}
在进行头删的时候我们想到的办法就是平移法:
就是把后后面的数据整体的向前推进一个位置,同时我们的size–;同样的也别忘记判断是否删完了。
4.自主设计
写到这里相信看到这的小伙伴们对我们的接口函数已经有基本的了解了,我们可以自己设计一些接口函数在这里我就给小伙伴们带来了三个个接口函数的设计:
- 给定一个下标要求查找数据,找到了返回下标否则返回-1。
- 给定一个下标要求在这个下标插入数据。
- 给定一个下标要求在这个下标删除数据。
函数定义:
//找到x并返回它的下标,找不到返回-1
int SeqListFind(SL* ps, SLDataType x);
//给定一个位置插入
void SeqListInsert(SL* ps, int pos, SLDataType x);
//给定一个位置删除
void SeeqListErase(SL* ps, int pos);
小伙伴们动起手来吧!!!!!!
我们会在下一节进行详细的讲解。
答案展示:博客链接
5.文件整体展示
5.1 seqList.h头文件展示
#pragma once //防止头文件被重复包含
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;// 表示数组中存储了多少个数据
int capacity;//数组实际能存放数据的空间间容量是多大
}SL;
// 接口函数--命名风格是跟着STL走的
//初始化
void SeqListPrint(SL* ps);
//打印数据
void SeqListInit(SL* ps);
//销毁空间
void SeqListDestory(SL* ps);
//检查容量
void SeqListCheckCapacity(SL* ps);
//尾插
void SeqListPushBack(SL* ps, SLDataType x);
//尾删
void SeqListPopBack(SL* ps);
//头插
void SeqListPushFront(SL* ps, SLDataType x);
//头删
void SeqListPopFront(SL* ps);
5.2 seqList.c源文件展示
#include "seqList.h"
//打印数据
void SeqListPrint(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
//初始化
void SeqListInit(SL* ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
//空间回收
void SeqListDestory(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//检查容量
void SeqListCheckCapacity(SL* ps)
{
//空间不足,扩容。
//SeqListCheckCapacity(ps);
if (ps->size == ps->capacity)//没空间或者空间满了
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//ps->capacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//
//if (ps->capacity == 0)
//{
// ps->capacity = 4;
//}
//else
//{
// ps->capacity = ps->capacity * 2;
//}
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (tmp == NULL)
{
printf("relloc fial\n");
exit(-1);//退出函数
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
//尾插
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
//printf("%d ", ps->a[ps->size]);
ps->size++;
}
//尾删
void SeqListPopBack(SL* ps)
{
//温柔的处理方式
//if (ps->size > 0)
//{
// ps->size--;
//}
//暴力的处理方式
assert(ps->size > 0);
ps->size--;
}
//头插
void SeqListPushFront(SL* ps, SLDataType x)
{
//判断是否满了
SeqListCheckCapacity(ps);
int end = 0;
for (end = ps->size - 1; end >= 0; end--)
{
ps->a[end+1] = ps->a[end];
}
ps->a[0] = x;
ps->size++;
}
//头删
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
int bigan = 1;
for (bigan = 1; bigan < ps->size; bigan++)
{
ps->a[bigan-1] = ps->a[bigan];
}
ps->size--;
}
5.3 test.c源文件展示
本章节主要是实现接口函数的,我们的test.c源文件只是用来检测我们的接口函数是否正确,所以不做要求。