数据结构-线性表的实现

 

顺序表的特点:要求数据从第一个位置开始放并且连续存储

顺序表缺陷:

1,空间不够需要增容,但增容是要付出代价的(比如增容的程度较小时,顺序表所在的内存区域后面还有足够的空间,可以原地扩容,但增容的程度较大时,顺序表所在内存区域可能就没有足够的空间了,这是就需要异地扩容,即换一个空间足够的内存区域,这是的代价就相对较大,因为需要将表中的每一个元素都复制过去)。

2,为避免溢出,需要在增加元素的过程中对其进行扩容,为避免频繁扩容,基本每次扩容都是将容量扩大二倍,这就可能导致一定的空间浪费。

3,顺序表要求数据从开始位置连续存储,那么想要在顺序表头部或者中部插入删除数据就需要挪动数据,效率不高。

顺序表的头文件seqlist.h(一般在头文件中声明,在源文件中实现,对于创建顺序表的结构,属于声明部分,即说明我所要建立的线性表中包含哪些结构、内容,因此这部分一般放在头文件中)

#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//#define N 10000
//typedef int SLDataType;
//
 静态顺序表
//typedef struct SeqList
//{
//	SLDataType a[N];
//	int size; // 表示数组中存储了多少个数据
//}SL;
//
 接口函数 -- 命名风格是跟着STL走的,建议大家也跟着我们上课走,方便后续学习STL
//void SeqListInit(SL* ps); 
//
 静态特点:如果满了就不让插入  缺点:给多少的合适呢?这个很难确定
 N给小了不够用,N给大了浪费
//void SeqListPushBack(SL* ps, SLDataType x);
//void SeqListPopBack(SL* ps);
//void SeqListPushFront(SL* ps, SLDataType x);
//void SeqListPopFront(SL* ps);
 ...

typedef int SLDataType;

// 动态顺序表
typedef struct SeqList//定义一个SeqList类型的结构体变量struct SeqList,并将其名称定义为SL
{
	SLDataType* a;//定义一个指向该结构体的指针a,该指针实际上是指向顺序表中的某一个元素,因此该指针的类型
	//就需要是顺序表中的元素类型,亦即SLDatatype类型
	int size;      // 表示数组中存储了多少个数据
	int capacity;  // 数组实际能存数据的空间容量是多大 
}SL;
//即定义一个表示顺序表的结构体变量时,内部要包括指向该结构体的指针,顺序表的容量和实际存储数据的数量

//头文件中都是声明,源文件中都是具体的实现,所以对于顺序表结构体的建立才放在头文件中

// 接口函数 -- 命名风格是跟着STL走的,建议大家也跟着我们上课走,方便后续学习STL
void SeqListPrint(SL* ps);//SL* ps代表指向结构体SL的指针变量,亦即结构体SL类型的指针变量
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);
// ...

// 找到了返回x位置下标,没有找打返回-1
int SeqListFind(SL* ps, SLDataType x);
// 指定pos下标位置插入
void SeqListInsert(SL* ps, int pos, SLDataType x);
// 删除pos位置的数据
void SeqListErase(SL* ps, int pos);

 顺序表的源文件,本文件中包含的是在头文件中所定义函数的具体实现

#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"//引用我定义的头文件seqlist.h

void SeqListPrint(SL* ps)//打印顺序表,函数需要的参数即指向该结构体的指针,而结构体中拥有函数所需要的一切信息,那么我也就不需要引用其它变量了,以下定义的这些函数都同理
{
	for (int 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;//将结构体中的一切都置为空,即将指针置为空指针,将数值置为0
}

void SeqListDestory(SL* ps)//顺序表的销毁
{
	free(ps->a);//将顺序表结构体中的指针指向的空间(并不改变指针的值)即将这部分空间返还给系统,指针还是原来的指针
	ps->a = NULL;
	ps->capacity = ps->size = 0;//将指针和数值置为空
}

void SeqListCheckCapacity(SL* ps)//检测顺序表的空间
{
	// 如果没有空间或者空间不足,那么我们就扩容
	if (ps->size == ps->capacity)//如果顺序表的容量等于顺序表中存储元素的个数
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//经典的a?b:c格式,将该式的运算结果赋值给整型变量newcapacity
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));//tmp即tmporary,临时的,定义一个该顺序表中变量类型的指针变量tmp,利用realloc函数重新分配一块空间,该空间的地址是顺序表
		//的地址,空间大小是单个元素所占字节数的newcapacity倍
		if (tmp == NULL)//利用malloc函数重新分配空间后,指向分配过后的区域的指针仍为空,那么说明申请空间失败,需要在程序中说明一下
		{
			printf("realloc fail\n");
			exit(-1);//exit() 结束当前进程/当前程序,在整个程序中,只要调用 exit ,就结束。exit(1)表示进程正常退出.返回 1;exit(0)表示进程非正常退出.返回 0.
		}

		ps->a = tmp;//将指针tmp所指向的区域赋给指针a
		ps->capacity = newcapacity;//将代表新空间容量的值赋给原先的代表容量的变量capacity
	}
}

void SeqListPushBack(SL* ps, SLDataType x)//顺序表尾插函数(头插和尾插都涉及到特定的元素,因此要比其他的函数多引用一个变量,即SLDatatype类型的变量x,这个x不包括在原结构体中,因此需要额外引用,相比之下,头删
//和尾删虽然都涉及元素的变动,但所有变量仍局限在原结构体的范围之内,因此仍只需要引用原结构体即可)
{
	SeqListCheckCapacity(ps);//直接引用上面定义的顺序表空间检测函数,保证本函数不会受空间不足的影响
	ps->a[ps->size] = x;//size即数组的容量,a[size]即现有数组最后一个元素再后一个,将这个位置赋值为x
	ps->size++;//赋值完毕后容量加一
}

void SeqListPopBack(SL* ps)//顺序表尾删函数
{
	// 温柔处理方式
	//if (ps->size > 0)
	//{
	//	//ps->a[ps->size - 1] = 0;
	//	ps->size--;
	//}

	// 暴力处理方式
	assert(ps->size > 0);//assert是断言函数,只有函数括号内的式子成立时,assert下面的程序才能正常运行
	ps->size--;
}

void SeqListPushFront(SL* ps, SLDataType x)//顺序表前插函数
{
	SeqListCheckCapacity(ps);//只要涉及到顺序表的增加,都要先保证空间

	// 挪动数据
	int end = ps->size - 1;//此时end的值等于顺序表最后一个元素的下表
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];//将该元素向后移动一位
		--end;
	}//最后一个循环,当end=0时,整个顺序表的第一个元素也被向后移动了一位,亦即整个顺序表全体都向后移动了一位,第一个位置就被空了出来,便于插入元素
	ps->a[0] = x;
	ps->size++;//这里要强调size加一的原因时因为前面定义的顺序表打印函数是以size的大小为依据来打印内容的
}

void SeqListPopFront(SL* ps)//顺序表头删函数
{
	assert(ps->size > 0);

	// 挪动数据
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];//从前往后,依次将除第一个元素外的每一个元素都向前移动一位
		++begin;
	}

	ps->size--;
}

函数的源文件(前面的两个文件做好了准备工作,源文件则利用这些实现我的目的)

#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"//引用我定义的头文件,这里不需要引用源文件seqlist.c,因为对于其中函数的调用无需额外说明,只需要在程序中直接使用即可

void TestSeqList1()
{
	SL sl;//这条语句的作用与int a一样,即定义SL型结构体变量sl
	SeqListInit(&sl);//初始化
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);//分别尾插1,2,3,4,5
	SeqListPrint(&sl);//打印出该顺序表

	SeqListPopBack(&sl);
	SeqListPopBack(&sl);
	SeqListPopBack(&sl);
	SeqListPopBack(&sl);
	SeqListPopBack(&sl);//分别尾删五个元素
	SeqListPrint(&sl);//打印出顺序表

	SeqListPushBack(&sl, 10);
	SeqListPushBack(&sl, 20);//再次尾插两个元素
	SeqListPrint(&sl);//打印出顺序表

	SeqListDestory(&sl);//将该顺序表置空
}

void TestSeqList2()
{
	SL sl;
	SeqListInit(&sl);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPrint(&sl);

	SeqListPushFront(&sl, 10);
	SeqListPushFront(&sl, 20);
	SeqListPushFront(&sl, 30);
	SeqListPushFront(&sl, 40);
	SeqListPrint(&sl);

	SeqListPopFront(&sl);
	SeqListPopFront(&sl);
	SeqListPrint(&sl);

	//SeqListDestory(&sl);
}

int main()
{
	//TestSeqList1();
	TestSeqList2();

	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值