数据结构----顺序表实现

一、顺序表的基本概念:

  • 顺序表是在计算机内存中以数组的形式保存的线性表。
  • 线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。

顺序表优点:1,存取速度高效,通过下标来直接存储;2,cpu高速缓存命中率更高。

顺序表缺点:1,在插入、删除时较慢;2,数据元素的个数不能自由扩充。

二、顺序表的分类 

静态顺序表:使用固定长度数组存储数据

typedef int SLDataType;
#define N 100
typedef struct SeqList  
{
	SLDataType a[N];    //如果N太小,空间不够用;太大,空间浪费
	int size;  //元素个数
}SL;

动态顺序表:在堆区上进行动态开辟的数组存储。

typedef int SLDataType;

typedef struct SeqList  
{
	SLDataType* a;   //指向动态数组的指针
	int size;  //元素个数
	int capacity; //容量
}SL;

 三、实现项目

本文章是用动态顺序表进行编写:养成良好的编码习惯,创建一个完整项目。我自己用的是vs2013。

1、创建:

  1. 在"头文件" 文件夹中创建 SeqList.h 用来存放头文件;
  2. 在 "源文件" 文件夹中创建 SeqList.c 用来实现函数;
  3. test.c 用来测试我们的动态顺序表;

 SeqList.h代码实现如下:

  1.  SeqList.h文件编写头文件、接口函数的声明、结构体的定义。
  2. 为了方便以后修改数据类型,增加代码的灵活性,可以使用 typedef 定义一个新的数据类型,取名为 SLDataType(表的数据类型)。
  3. 为了让定义的结构体使用时更方便,也可以使用 typedef 将表名 struct SeqList,后续在使用时可以更加方便,其定义为 SL,将名字变短。
#pragma once   //防止头文件被重复包含
#include <stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLDataType;

typedef struct SeqList  
{
	SLDataType* a;   //指向动态数组的指针
	int size;  //元素个数
	int capacity; //容量
}SL;

void SeqListInit(SL * ps);  //初始化
void SeqListPushback(SL * ps, SLDataType x);   //尾插
void SeqListPopback(SL * ps);  //尾删
void SeqListPushFront(SL * ps,SLDataType x);  //头插  
void SeqListPopFront(SL * ps);  //头删
int  SeqListFind(SL* ps, SLDataType x); //查找 ,找到了返回x下标
void SeqListInsert(SL* ps, int pos, SLDataType x); //指定pos下标插入
void SeqListErase(SL* ps, int pos); //删除pos位置的数据
void SeqListcheckCapacity(SL* ps); //动态扩容
void SeqListDestory(SL* ps);  //释放空间
void SeqListprintf(SL* ps); //打印

2、初始化 

先引入头文件#include"SeqList.h",初始化结构体,将数组为空、有效数据个数和数组能存数据的空间容量一并初始化为0。

#include "SeqList.h"
void SeqListInit(SL* ps) //初始化
{
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}

3、扩容

  • 后续的尾插和头插都会使用此函数接口,对于公共函数进行封装,方便后续复用。
  • 如果空间不够,将使用realloc函数进行空间扩容。
  • 代码中newcapacitv是扩容后的内存空间,tmp是指向这个新的空间的指针。如果空间为0,则扩容可以放置4个元素的空间,如果空间已满,则在原来的空间基础上*2。
void SeqListcheckCapacity(SL* ps) //扩容
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;   //扩容
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity*sizeof(SLDataType));
		if (tmp == NULL)
		{
			printf("relloc fail\n");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
}

4、尾插、尾删

尾插

有三种情况:

  1.  第一种情况是顺序表压根就没有空间。
  2. 第二种情况就是我们创建的 capacity 空间满了。
  3.  第三种情况是空间足够,直接插入数据即可。
void SeqListPushback(SL * ps, SLDataType x) //尾插
{
	//如果没有空间或者空间不足,那么就需要进行扩容
	SeqListcheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;

}

尾删:

即删除最后一个元素,只需要元素数量减去一个,即size--,这样我们就无法访问最后一个元素了,它便是无效的数据(逻辑删除)。

注意:不管是进行头删还是进行尾删,我们都要检查ps->size是否大于0。(断言)

void SeqListPopback(SL * ps)  //尾删
{
	assert(ps->size);
	ps->size--;
}

5、头插、头删

头插:

顺序表要求数据是连续存储的,且必须是从头开始存储。

所以,对于顺序表而言如果要实现头插,就需要把数据往后挪动。不能从前往后挪,如果从前往后挪就挪就会把后面的数据覆盖掉。 

void SeqListPushFront(SL * ps,SLDataType x)  // 头插
{
	SeqListcheckCapacity(ps);  //检查空间
	//先挪动数据
	int end = ps->size - 1;
	while (end>=0)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[0] = x;
	ps->size++;
}

头删:

思路和头插类似,依次向前挪动。

删除数据不要忘记暴力检查ps->size。 

void SeqListPopFront(SL* ps)  //头删
{
	assert(ps->size);
		int begin = 1;  //定义一个下标
		while (begin < ps->size)
		{
			ps ->a [begin - 1] = ps->a[begin];
			++begin;
		}
		ps->size--;
}

6、查找

int  SeqListFind(SL* ps, SLDataType x)  //查找
{
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return x;
		}
	}
	return -1;
}

7、指定下标插入

顺序表要求数据从第一个开始放并且数据是连续存储的,就要注意下指定的下标位置 pos 的位置,需要进行一些检查。

void SeqListInsert(SL* ps, int pos, SLDataType x)  //指定下标插入
{
	assert(pos<=ps->size&&pos>=0);
	SeqListcheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos]=x;
	ps->size++;

}

 指定下标位置插入,是可以替代头插和尾插。(函数复用

8、删除指定位置

删除指定位置的数据,也要限制 pos 的位置,因为 ps->size 这个位置没有效数据,所以删除的位置小于 psl->size。

void SeqListErase(SL* ps, int pos)  //删除指定位置
{
	assert(pos >= 0 && pos < ps->size);
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	ps->size--;
}

 指定位置删除的函数可以替代头删和尾删。(函数复用

9、释放动态开辟空间

动态开辟是在堆区上进行的,所以空间不用就需要销毁。不销毁会存在内存泄漏的风险。

void SeqListDestory(SL* ps) //释放动态开辟空间
{
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}

10、打印

void SeqListprintf(SL* ps)  //打印
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

¥sunrise

来自大牛的认可,是我梦寐以求的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值