数据结构——顺序表

        

目录

       一. 什么是顺序表?

        1,静态顺序表

        2,动态顺序表

①动态顺序表的实现及其初始化

②空间的创建

③顺序表的打印和销毁

④顺序表的尾部插入和删除

 ⑤顺序表的头部插入和删除

⑥顺序表pos位置的插入和删除

 ⑦顺序表指定元素的删除

        二,整体代码


        开始进入数据结构的篇章啦!!!!这篇文章想来介绍一下顺序表这一数据结构,它属于线性表的一种,线性表是一种线性结构——可以理解为一条连续的直线。那么什么是线性表呢? 线性表是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串等。

       一. 什么是顺序表?

        顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。这里可以看出,顺序表与我们C语言的数组是类似的,存储相同的数据在一段连续的空间,而顺序表有另一要求——要求数据连续存储的,不能跳跃间隔。

        顺序表一般可以分为:静态顺序表动态顺序表

静态顺序表是用固定长度的数组来存储元素,动态顺序表是用可以动态开辟的数组存储元素。

        1,静态顺序表

        我们以静态表为例:它需要一个指定大小的数组,但数组的每个元素并不是都需要存放,所以我们需要一个变量来记录表中元素个数,类似于我们的通讯录一般。所以这里我们就需要一个结构体变量,基本实现如下:

typedef struct staticseqlist
typedef int SLDateType;
{
    SLDateType arr[10];//数据的空间大小
    int sz;//有效数据的个数
}SL;

这里我们为了方便,重命名该结构体为SL,typedef是由于我们无法确定该数组存储的元素类型,方便修改数据。这里可以观察到,静态顺序表只适用于确定知道需要存多少数据的场景。如果静态顺序表的的数组空间开多了,就会造成浪费,开少了又不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。这里我们

        2,动态顺序表

①动态顺序表的实现及其初始化

typedef int SLDateType;
typedef struct SeqList
{
	SLDateType* a;//开辟空间
	int sz;//有效元素个数
	int capacity;//总大小空间
}SL;

//初始化
void SLInit(SL* ps)
{
	ps->a = NULL;
	ps->sz = 0;
	ps->capacity = 0;
}

②空间的创建

void SLCheckcapacity(SL* ps)
{
	if(ps->sz == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
}

         在初始化中我们定义的都是NULL和0值,但存储数据免不了空间的申请创建,于是我们实现一个空间开辟函数,基本逻辑是:若元素个数和空间开始都为0,就开辟一段四个SLDateType大小的空间;若元素个数已经等于表的大小,则重新开辟两倍的原空间以供使用。

③顺序表的打印和销毁

由于我们要对顺序表进行增删查改,我们需要一个打印函数来观察其元素的时刻变化,其次,动态开辟空间时内存是不会主动释放的,所以我们需要一个释放空间的函数来进行对空间进行自主释放,实现如下:

//打印顺序表
void SLprint(SL* ps)
{
	int i = 0;
	for (i = 0; i < ps->sz; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}


//程序的销毁
void SLDestroy(SL* ps)
{
	assert(ps);
	if(ps->a)
	{
		ps->a = NULL;
		ps->sz = 0;
		ps->capacity = 0;
	}
}

④顺序表的尾部插入和删除

由于顺序表示一段连续开辟的空间,所以我们可以从它的尾部往表中插入数据,在容量未满时我们直接插入,容量满后我们开辟新的空间再插入。实现如下:

void SLPushback(SL* ps,SLDateType x)
{
	assert(ps);
	SLCheckcapacity(ps);
	ps->a[ps->sz] = x;
	ps->sz++;
}

起初我们需要检验ps是否为空,然后进行空间检查,将x的值放入开辟的空间中。

再来进行尾部删除操作,由于我们空间是连续的,在删除尾部元素时只需要将开辟的空间大小减一然后程序结束后释放开辟的空间即可

void SLPopback(SL* ps)
{
	assert(ps->sz > 0);
	ps->sz--;
}

整体的实现效果如下:

数据结构——顺序表_第1张图片

 数据结构——顺序表_第2张图片

 ⑤顺序表的头部插入和删除

我们也可以将数据从表的头部插入,实现逻辑如下:

数据结构——顺序表_第3张图片

void SLPushFront(SL* ps,SLDateType x)
{
	assert(ps);
	SLCheckcapacity(ps);
	int end = ps->sz - 1;
	while (end>=0)
	{
		ps->a[end+1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->sz++;
}

从头部删除也是同样类似的逻辑,但这里需要注意,如果sz是0的话——也就是没有元素可以删除了,我们会进行ps->sz--这一步,这样会使得sz变为-1,在我们后面的内存释放过程中遇到free函数时,就会报错,因为这时sz是-1。所以我们需要断言ps->sz是否大于0。实现如下:

void SLPopFront(SL* ps)
{
	assert(ps);
	//assert(ps->sz > 0);
	int begin = 1;
	while (begin < ps->sz)
	{
		ps->a[begin-1] = ps->a[begin];
		begin++;
	}
	ps->sz--;
}

效果如下:

数据结构——顺序表_第4张图片

 数据结构——顺序表_第5张图片

⑥顺序表pos位置的插入和删除

我们的pos要使用合法,即——0<=pos<=sz,因为pos可以插入在最后一个元素的末尾,所以我们是sz而不是sz-1,实现如下:

SLInsert(SL* ps, int pos , SLDateType x)
{
	assert(ps);
	assert(pos > 0);
	assert(pos<=ps->sz);
	SLCheckcapacity(ps);
	int end = ps->sz-1;
	while (end>=pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->sz++;
}

可以观察到与我们的头部插入逻辑极其相似,而当pos取0的时候就是头插,pos取sz的时候就是尾插所以我们可以改变代码如下:

//头部插入
void SLPushFront(SL* ps,SLDateType x)
{
    SLInsert(ps, 0, x);
}

//尾部插入
void SLPushback(SL* ps,SLDateType x)
{
	SLInsert(ps, ps->size, x);
}

删除则与头部删除也类似,实现如下:

SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos > 0);
	assert(pos <= ps->sz);
	int begin = pos;
	while (begin < ps->sz - 1)
	{
		ps->a[begin] = ps->a[begin+1];
		begin++;
	}
	ps->sz--;
}

类似的,也可以将头部删除和尾部删除改为如下:

//尾删
void SLPopback(SL* ps)
{
	SLErase(ps, ps->size-1);
}

//头删
void SLPopFront(SL* ps)
{
	SLErase(ps, 0);
}

实现效果如下: 

数据结构——顺序表_第6张图片

 数据结构——顺序表_第7张图片

 ⑦顺序表指定元素的删除

当我们要删除指定元素的时候呢,应该如何实现,只需要遍历顺序表找到该元素,然后删除即可,创建一个查找函数,实现如下:

int SLFind(SL* ps, int pos)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->sz; i++)
	{
		if (ps->a[i] == pos)
		{
			return i;
		}
	}
	return -1;
}

效果如下:

数据结构——顺序表_第8张图片

 于此,我们便实现了整个动态顺序表的增删查改等一系列操作。

        二,整体代码

我们在这里创建一个工程

  • SeqList.h(顺序表接口函数声明和引用的头文件)
  • SeqList.c(顺序表接口函数的实现)
  • Test.c(测试)

数据结构——顺序表_第9张图片

 代码如下:

//seqlist.c
#include"Seqlist.h"

//初始化顺序表
void SLInit(SL* ps)
{
	ps->a = NULL;
	ps->sz = 0;
	ps->capacity = 0;
}

//打印顺序表
void SLprint(SL* ps)
{
	int i = 0;
	for (i = 0; i < ps->sz; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

//程序的销毁
void SLDestroy(SL* ps)
{
	assert(ps);
	//if (ps->a != NULL)
	if(ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->sz = ps->capacity = 0;
	}
}

//检查空间
void SLCheckcapacity(SL* ps)
{
	if (ps->sz == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
}

//尾插
void SLPushback(SL* ps,SLDateType x)
{
	assert(ps);
	SLCheckcapacity(ps);
	ps->a[ps->sz] = x;
	ps->sz++;
}

//尾删
void SLPopback(SL* ps)
{
	assert(ps->sz > 0);
	ps->sz--;
}

//头插
void SLPushFront(SL* ps,SLDateType x)
{
	assert(ps);
	SLCheckcapacity(ps);
	int end = ps->sz - 1;
	while (end>=0)
	{
		ps->a[end+1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->sz++;
}

//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->sz > 0);
	int begin = 1;
	while (begin < ps->sz)
	{
		ps->a[begin-1] = ps->a[begin];
		begin++;
	}
	ps->sz--;
}

//pos位置插入,删除
SLInsert(SL* ps, int pos , SLDateType x)
{
	assert(ps);
	assert(pos > 0);
	assert(pos<=ps->sz);
	SLCheckcapacity(ps);
	int end = ps->sz-1;
	while (end>=pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->sz++;
}

//删除pos的数据
SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos > 0);
	assert(pos <= ps->sz);
	int begin = pos;
	while (begin < ps->sz - 1)
	{
		ps->a[begin] = ps->a[begin+1];
		begin++;
	}
	ps->sz--;
}

//找到指定的数据
int SLFind(SL* ps, int pos)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->sz; i++)
	{
		if (ps->a[i] == pos)
		{
			return i;
		}
	}
	return -1;
}


//seqlist.h
#include
#include
#include
typedef int SLDateType;
typedef struct SeqList
{
	SLDateType* a;
	int sz;
	int capacity;
}SL;

//初始化顺序表
void SLInit(SL* ps);

//打印顺序表
void SLprint(SL* ps);

//程序的销毁
void SLDestroy(SL* ps);

//尾插
void SLPushback(SL* ps, SLDateType x);

//尾删
void SLPopback(SL* ps);

//头插
void SLPushFront(SL* ps, SLDateType x);

//检查空间
void SLCheckcapacity(SL* ps);

//头删
void SLPopFront(SL* ps);

//特定位置插入
SLInsert(SL* ps, int pos, SLDateType x);

//删除指定位置
SLErase(SL* ps, int pos);

//找到指定位置
int SLFind(SL* ps, int pos);


//test.c
#include"Seqlist.h"
void Test1()
{
	SL s1;
	SLInit(&s1);
	SLPushback(&s1, 1);
	SLPushback(&s1, 2);
	SLPushback(&s1, 3);
	SLPushback(&s1, 4);
	SLprint(&s1);
	SLDestroy(&s1);
}

void Test2()
{
	SL s1;
	SLInit(&s1);
	SLPushback(&s1, 1);
	SLPushback(&s1, 2);
	SLPushback(&s1, 3);
	SLPushback(&s1, 4);
	SLPopback(&s1);
	SLprint(&s1);
	SLDestroy(&s1);
}

void Test3()
{
	SL s1;
	SLInit(&s1);
	SLPushFront(&s1, 1);
	SLPushFront(&s1, 2);
	SLPushFront(&s1, 3);
	SLPushFront(&s1, 4);
	SLPopFront(&s1);
	SLprint(&s1);
	SLDestroy(&s1);
}

void Test4()
{
	SL s1;
	SLInit(&s1);
	SLPushFront(&s1, 1);
	SLPushFront(&s1, 2);
	SLPushFront(&s1, 3);
	SLPushFront(&s1, 4);
	SLInsert(&s1,4,100);
	int pos = SLFind(&s1, 3);
	if(pos != -1)
	{
		SLInsert(&s1, pos, 100);
	}
	SLprint(&s1);
	SLDestroy(&s1);
}

void Test5()
{
	SL s1;
	SLInit(&s1);
	SLPushFront(&s1, 1);
	SLPushFront(&s1, 2);
	SLPushFront(&s1, 3);
	SLPushFront(&s1, 4);
	int pos = SLFind(&s1, 1);
	if (pos != -1)
	{
		SLErase(&s1, pos);
	}
	SLprint(&s1);
	SLDestroy(&s1);
}

int main()
{
	Test1();
	Test2();
	Test3();
	Test4();
	Test5();
	return 0;
}

你可能感兴趣的:(数据结构,数据结构)