顺序表之初

在这里插入图片描述

欢迎来到我的:世界

希望作者的文章对你有所帮助,有不足的地方还请指正,大家一起学习交流 !


目录

  • 线性表简介
    • 顺序表定义
    • 动态顺序表的初始化
    • 尾插
    • 头插
    • Cheak 判断是否增容
    • 尾删:
    • 头删:
    • 打印
    • 在pos位置前插入x
    • 删除pos位置的值
    • 查找
    • 修改
    • 销毁

线性表简介

百度:线性表最基本、最简单、也是最常用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。

  • 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
  • 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。

顺序表之初_第1张图片


顺序表定义

顺序表是用一段物理地址连续的储存单元依次储存数据结构的线性结构,一般情况采用的是数组储存;在数组上完成数据的增删查改;

顺序表可以分为:
1.静态顺序表:使用固定长数组存储元素;

#define N 100
typedef int SLDateType;
//定义静态顺序表
typedef struct SeqList
{
	SLDateType a[N]; //固定数组大小,N的值无法改变
	int size;        //数组有效的个数
	
}SeqList;

2.动态顺序表:使用动态开辟的数组储存;

typedef int SLDateType;
//定动态义顺序表
typedef struct SeqList
{
	SLDateType* a;//指向动态数组的指针
	int size;     //数组的有效数据
	int capacity; //空间大小
}SeqList;

动态顺序表的初始化

初始化顺序表是使用动态分配数组空间方式构造一个空的线性表。

//初始化
void SeqListInit(SeqList* ps)
{
	//用 malloc申请一块空间
	ps->a = (SLDateType*)malloc(sizeof(SLDateType) * 4);
	if (ps->a== NULL)//判断是否申请空间成功
	{
		perror("malloc");
		exit(-1);
	}
	
	ps->capacity = 4;//将顺序表的最大长度设为 4
	ps->size = 0; //将顺序表的当前长度设为 0

}

有关malloc函数申请动态内存空间,可以看我的另一篇博客动态内存空间管理,有详细解释;

尾插

尾插就是在最后一个元素的后面插入一个元素

  • 尾插有三种情况:
    第一种:情况是顺序表压根就没有空间,用assert解决;
    第二种:情况就是我们创建的 capacity 空间满了,需要增容再往后插入;
    第三种:情况是空间足够,直接插入数据即可;
void SeqListPushBack(SeqList* ps, SLDateType x)
{
	//每次进来判断依次满了要扩容
	if (ps->size  == ps->capacity)
	{
		//realloc函数是专门用来增容的,以两倍的增容
		SLDateType* tem = (SLDateType*)realloc(ps->a, ps->capacity * 2  * sizeof(SLDateType));
		if (tem == NULL)//判断是否增容成功
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = tem;
		ps->capacity *= 2;//空间大小以两倍增容
	}

	ps->a[ps->size] = x;//运用下标存储要插入的值
	ps->size++;//尾插成功则有效数据就加 1
}

有关realloc函数调整动态申请的内存空间,详解也再我的另一篇博客中:动态内存空间管理,其详细解释,一定要看懂!很重要!!

头插

顺序表要求数据是连续存储的,且必须是从头开始存储。所以,对于顺序表而言如果要实现头插,就需要把数据往后挪动。不能从前往后挪,如果从前往后挪就挪就会把后面的数据覆盖掉。
顺序表之初_第2张图片

void SeqListPushFront(SeqList* ps, SLDateType x) {

	//每次进来判断依次满了要扩容
	if (ps->size == ps->capacity)
	{
		//realloc函数是专门用来增容的,以两倍的增容
		SLDateType* tem = (SLDateType*)realloc(ps->a, ps->capacity * 2 * sizeof(SLDateType));
		if (tem == NULL)//判断是否增容成功
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = tem;
		ps->capacity *= 2;//空间大小以两倍增容
	}
	//从后往前式的往后移动一个元素
	int end = ps->size - 1;
	while (end>=0)
	{
		ps->a[end+1] = ps->a[end];
		--end;
	}

	ps->a[0] = x;//插入首元素
	ps->size++;//头插成功有效元素加1
}

这个时候你回头会发现,如果要增容的时的代码几乎一致,我们可以为要增容单独写一个函数,判断是否要增容:

Cheak 判断是否增容

void Cheak(SeqList*ps)
{
	//每次进来判断依次满了要扩容
	if (ps->size == ps->capacity)
	{
		//realloc函数是专门用来增容的,以两倍的增容
		SLDateType* tem = (SLDateType*)realloc(ps->a, ps->capacity * 2 * sizeof(SLDateType));
		if (tem == NULL)//判断是否增容成功
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = tem;
		ps->capacity *= 2;//空间大小以两倍增容
	}
}

而这个时候的尾插、头插这样写:

头插:

void SeqListPushFront(SeqList* ps, SLDateType x) {
	Cheak(ps);//判断增容
	//从后往前式的往后移动一个元素
	int end = ps->size - 1;
	while (end>=0)
	{
		ps->a[end+1] = ps->a[end];
		--end;
	}

	ps->a[0] = x;//插入首元素
	ps->size++;//头插成功有效元素加1
}

尾插:

void SeqListPushBack(SeqList* ps, SLDateType x)
{
	Cheak(ps);//判断增容

	ps->a[ps->size] = x;//运用下标存储要插入的值
	ps->size++;//尾插成功则有效数据就加 1
}

尾删:

顺序表的尾删比较简单,直接让size–就可以;

void SeqListPopBack(SeqList* ps)
{
	//当顺序表中无元素,不会进行尾删
	if (ps->size == 0)
	{
		printf("size越界访问");
		exit(-1);
	}

	ps->size--;
}

头删:

头删可以理解为:直接将从第二元素从前往后的覆盖上去;

//头删
void SeqListPopFront(SeqList* ps)
{
	
	assert(ps->size > 0);

	//删减
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}

打印

打印出顺序表中的元素;

void SeqListPrint(SeqList* ps) {

	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

在pos位置前插入x

需要知道pos为元素下标,找到pos位后,然后将其后面的所有元素向后移动一个元素,再将要插入的x插入pos位置;

void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
	//检查pos位置是否在顺序表里面
	assert(pos >= 0 && pos <= ps->size);
	Cheak(ps);//判断是否增容
	//找到有效数据元素的下标
	int end = ps->size - 1;
	//往后移
	while (end>=pos)
	{
		ps->a[end+1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;//空出pos位后插入
	ps->size++;
}

注意:SeqListInsert这个函数可以代替尾插或头插;

删除pos位置的值

删除指定位置的数据,我们仍然要限制 pos 的位置。限制条件部分和 SeqListInsert 不同的是,因为 psl->size 这个位置没有效数据,所以删除的位置不能是 psl->size!

void SeqListErase(SeqList* ps, int pos) {
	//检查pos位置是否在顺序表里面
	assert(pos >= 0&&pos< ps->size);
	//找到pos位置的下一个
	int begain = pos + 1;
	
	while (begain < ps->size)
	{
		ps->a[begain - 1] = ps->a[begain];
		begain++;
	}
	ps->size--;

}

注意:SeqListErase这个函数可以代替尾删或头删;

查找

//查找返回其下标,否则返回-1;
int SLFind(SL* ps, SLDatatype x)
{
	assert(ps);
	for (int i = 0; i <= ps->size; i++)
	{
		if (ps->a[i] == x)
			return i;
	}
	return -1;
 
}

修改

修改pos位置的值

//修改pos位置的值,pos是其下标
void SLModify(SL* ps, int pos, SLDatatype x)  
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
 
	ps->a[pos] = x;
 
}

看代码运行结果:
顺序表之初_第3张图片
最后的是:

销毁

如果进行了动态内存的内存空间申请,一定要记得销毁,因为动态内存空间是不会主动释放的,除非程序结束,否则可能会存在内存泄漏的风险;

//销毁
void SeqListDestroy(SeqList* ps)
{
	//释放
	free(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->size = 0;

}

到了最后:感谢支持

我还想告诉你的是:
------------对过程全力以赴,对结果淡然处之
也是对我自己讲的

你可能感兴趣的:(c语言,开发语言,c++,数据结构,链表)