线性表☞顺序表篇(7000字细致入微讲解)

  个人主页:欢迎大家光临——>沙漠下的胡杨

  各位大帅哥,大漂亮

 如果觉得文章对自己有帮助

 可以一键三连支持博主

 你的每一分关心都是我坚持的动力

 

 ☄: 本期重点:线性表中的顺序表

  希望大家每天都心情愉悦的学习工作。 

什么是线性表?

什么是顺序表呢?

1.创建多文件项目

2.创建结构体

3.初始化数组

4.开辟动态空间 (增容)

5. 顺序表尾插

6. 顺序表的尾删

7.顺序表的头插

8.顺序表的头删

9.任意位置插入

10。删除任意位置

11,顺序表的查找

12.顺序表的数据修改

13.补充下删除和修改部分

14 头删尾删和头插尾插的优化

16.free开辟的空间和打印

17:关于整体代码

Seqlist.h文件

Seqlist.c文件

18:关于所用的知识:

下期预告:


什么是线性表?

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

什么是顺序表呢?

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

1. 静态顺序表:使用定长数组存储元素。(长度固定)

2. 动态顺序表:使用动态开辟的数组存储。(可以扩容)

1.创建多文件项目

我们要通过多文件实现顺序表。

要有个头文件,和函数实现的 .c 文件 和 测试的源文件。

其中,头文件中要防止头文件重复包含,和头文件的引用。

2.创建结构体

我们要创建一个结构体来存放和进行顺序表相关操作。

typedef int SLdataType;

typedef struct Seqlist
{
	SLdataType *a;
	int size;
	int capicity;
}SL;

我们创建了结构体,并且进行结构体的重命名,

然后我们要进行使用动态开辟的内存,所以我们要有一个指针变量,存放动态开辟的数组.

接着我们还有有已经存放元素的个数,和实际的容量大小。

最后,我们要考虑我们存什么数组呢?是整形,字符,浮点?这些不确定所以我们要把类型在进行重定义下,是为了我们可以更方便的维护数组。

3.初始化数组

我们有数组了,那么首先就要进行数组的初始化。

void SLInit(SL* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capicity = ps->size = 0;
}

我们要知道函数的声明要放入头文件(后续不在赘述),而函数的实现要放到 .c 文件中。

其次我们要进行函数的 "预防式" 编程,就是要进行判断传入函数指针的 "合法性"。

4.开辟动态空间 (增容)

我们最重要的动态的数组还没有呢?

我们要进行开辟,那么通过malloc开辟还是其他方式呢?

malloc开辟之后还要考虑增容问题,那么我们直接用realloc开辟不是更好

所以我们这样开辟:

void SLCheckCapaticy(SL *ps)
{
	assert(ps);

	if (ps->size == ps->capicity)
	{
        //使用双目操作符让增容和初始化一个realloc函数实现。
		int newCapicity = ps->capicity == 0 ? 4 : 2 * (ps->capicity);
		SLdataType * tmp = (SLdataType *)realloc(ps->a, sizeof(SLdataType)*newCapicity);
		if (NULL == tmp)
		{
			perror("realloc:");
			exit(-1);
		}
		ps->a = tmp;
		ps->capicity = newCapicity;
	}
}

这样就可以用一个函数来实现开辟空间和增容的过程。

5. 顺序表尾插

尾插函数的实现比较简单,但是有一点要注意,就是判断否要增容。

void SLPushBack(SL* ps, SLdataType x)
{
	assert(ps);

	SLCheckCapaticy(ps);

	ps->a[ps->size] = x;
	ps->size++;
}

先判断函数的指针是否为空,在判断是否要增容,最后直接放数据就好啦。

6. 顺序表的尾删

尾删时,我们需要把删除的值置为 0 或者 -1吗?

答案是不需要,因为如果数据本来就是0 或 1呢,那么不就没有变吗?

所以我们只需要把结构体中的元素个数进行 -1 就可以了。

void SLPopBack(SL *ps)
{
	assert(ps);

	assert(ps->size);

	ps->size--;
	
}

7.顺序表的头插

顺序表的头插,就是在下标为 0 位置处进行写入,首先我们应该把 下标为 0 到下标为size 数

据向后移动一个数据大小的空间,把下标为 0 的空间腾出来。所以我们要进行数据的移动,

并且,我们要把数据从后向前的进行向后移动。

我们先创建一个变量作为下标,

这个下标不能够越界,并且要访问到0~size-1的元素,这就是循环条件。

然后就是不能越界,还要考虑扩容的问题。

最后放入元素,size++,就可以了。

void SLPushFront(SL *ps, SLdataType x)
{
	assert(ps);

	SLCheckCapaticy(ps);

	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end+1] = ps->a[end];
		end--;
	}

	ps->a[0] = x;
	ps->size++;

	
}

8.顺序表的头删

头删和头插很像,但是有点不一样,限制条件更多了。

首先也要判断指针是否为空。

判断元素个数是否大于等于 0(也用assert进行断言)

头删只需要进行元素覆盖,不需要改元素。

移动元素进行覆盖。(元素是0到size-1,防止向前越界,限制条件为 1到 szie -1)

 

void SLPopFront(SL *ps)
{
	assert(ps);

	assert(ps->size);
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;

}

9.任意位置插入

任意位置插入的参数要多一个下标。

首先我们要判断指针和下标是否合法。

其次要考虑是否会越界,要进行扩容。

然后进行数据移动,限制和头插类似。(pos 到 size -1,实际访问pos到size)

 最后放数据,size++

void SLInsert(SL* ps, int pos, SLdataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);

	SLCheckCapaticy(ps);

	int end = ps->size - 1;

	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}

	ps->a[pos] = x;
	ps->size++;

}

10。删除任意位置

删除任意位置和插入的参数一样。

不需要考虑扩容,但是要考虑向前越界

 数据移动:限制条件是 (pos 到 size-1,实际访问pos 到size)

最后把size--即可。

void SLErase(SL *ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	int begin = pos;

	while (begin < ps->size-1)
	{
		ps->a[begin] = ps->a[begin+1];
		begin++;
	}
	ps->size--;
}

11,顺序表的查找

查找的代码很简单,只需要遍历进行查找就可以啦。

需要注意的是返回的是找到数据的下标

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;
}

12.顺序表的数据修改

 修改也很简单,我们直接进行数据的覆盖即可

需要注意的是下标的限制条件。1

void SLModify(SL *ps, int pos, SLdataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	ps->a[pos] = x;
}

13.补充下删除和修改部分

我们进行顺序表的查找和修改时,在进行数据的输入时,可以封装成函数。

有一点要注意的是,查找函数的返回值是可以作为判断的条件,以及可以作为链式访问的

void Find(SL *ps)//查找相关的函数
{
	printf("请输入要查找的数据:");
	int a = 0;
	scanf("%d", &a);
	if (SLFind(ps, a) >= 0)
	{
		printf("找到了,下标是:%d\n", SLFind(ps, a));
		SLPrint(ps);
	}
	else
	{
		printf("找不到\n");
	}
}

void Modify(SL *ps)//修改相关的函数
{
	printf("要查找的数据和要替换的数据:");
	int x = 0;
	int y = 0;
	scanf("%d%d", &x, &y);
	if (SLFind(ps, x) >= 0)
	{
		SLModify(ps, SLFind(ps, x), y);
		printf("修改成功\n");
		SLPrint(ps);
	}
	else
	{
		printf("没找到:%d\n", x);
	}
}

14 头删尾删和头插尾插的优化

我们已经写过了任意位置插入,那么我们是不是可以把头插尾插和头删和尾删进行下优化

头插:

void SLPushFront(SL *ps, SLdataType x)
{
	//assert(ps);

	//SLCheckCapaticy(ps);

	//int end = ps->size - 1;
	//while (end >= 0)
	//{
	//	ps->a[end+1] = ps->a[end];
	//	end--;
	//}

	//ps->a[0] = x;
	//ps->size++;

	SLInsert(ps, 0, x);//从0下标插入
}

尾插:

void SLPushBack(SL* ps, SLdataType x)
{
	assert(ps);

	SLCheckCapaticy(ps);

	ps->a[ps->size] = x;
	ps->size++;

	//SLInsert(ps, ps->size, x);//从size位置插入,不会越界
}

头删:

void SLPopFront(SL *ps)
{
	assert(ps);

	assert(ps->size);
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;

	//SLErase(ps, 0);//从0下标开始删
}

尾删

void SLPopBack(SL *ps)
{
	assert(ps);

	assert(ps->size);

	ps->size--;
	//SLErase(ps, ps->size - 1);//删除size-1位置的数据
}

这就是代码的可复用性

16.free开辟的空间和打印

都没啥说的:

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

	if (ps->a != NULL)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capicity = ps->size = 0;
	}
}

17:关于整体代码

Seqlist.h文件

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


typedef int SLdataType;

typedef struct Seqlist
{
	SLdataType *a;
	int size;
	int capicity;
}SL;


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

void SLCheckCapaticy(SL *ps);//增容

void SLPrint(SL *ps);//打印

void SLDestory(SL *ps);//free空间

void SLPushBack(SL* ps,SLdataType x);//尾插

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

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

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

void SLInsert(SL* ps, int pos, SLdataType x);//任意位置插入

void SLErase(SL *ps, int pos);//任意位置删除

int SLFind(SL *ps, SLdataType x);//查找
void Find(SL *ps);

void SLModify(SL *ps, int pos, SLdataType x);//修改
void Modify(SL *ps);

Seqlist.c文件

#include "Seqlist.h"

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

void SLInit(SL* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capicity = ps->size = 0;
}

void SLCheckCapaticy(SL *ps)
{
	assert(ps);

	if (ps->size == ps->capicity)
	{
		int newCapicity = ps->capicity == 0 ? 4 : 2 * (ps->capicity);
		SLdataType * tmp = (SLdataType *)realloc(ps->a, sizeof(SLdataType)*newCapicity);
		if (NULL == tmp)
		{
			perror("realloc:");
			exit(-1);
		}
		ps->a = tmp;
		ps->capicity = newCapicity;
	}
}

void SLPushBack(SL* ps, SLdataType x)
{
	assert(ps);

	SLCheckCapaticy(ps);

	ps->a[ps->size] = x;
	ps->size++;

	//SLInsert(ps, ps->size, x);
}

void SLPushFront(SL *ps, SLdataType x)
{
	//assert(ps);

	//SLCheckCapaticy(ps);

	//int end = ps->size - 1;
	//while (end >= 0)
	//{
	//	ps->a[end+1] = ps->a[end];
	//	end--;
	//}

	//ps->a[0] = x;
	//ps->size++;

	SLInsert(ps, 0, x);
}


void SLPopBack(SL *ps)
{
	assert(ps);

	assert(ps->size);

	ps->size--;
	//SLErase(ps, ps->size - 1);
}

void SLPopFront(SL *ps)
{
	assert(ps);

	assert(ps->size);
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;

	//SLErase(ps, 0);
}


void SLDestory(SL *ps)
{
	assert(ps);

	if (ps->a != NULL)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capicity = ps->size = 0;
	}
}

void SLInsert(SL* ps, int pos, SLdataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);

	SLCheckCapaticy(ps);

	int end = ps->size - 1;

	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}

	ps->a[pos] = x;
	ps->size++;

}


void SLErase(SL *ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	int begin = pos;

	while (begin < ps->size-1)
	{
		ps->a[begin] = ps->a[begin+1];
		begin++;
	}
	ps->size--;
}

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;
}

void SLModify(SL *ps, int pos, SLdataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	ps->a[pos] = x;
}

18:关于所用的知识:

下面知识不太清楚的兄弟们,可以点击下面的链接,都是小编自己撰写的,欢迎关顾~!

指针相关的知识----->指针知识

内存管理相关知识讲解------>内存管理

结构体相关知识------->结构体知识

下期预告:

下期讲解函数栈帧的调用

下期憋个大的~!~!~!

猜你喜欢

转载自blog.csdn.net/m0_64770095/article/details/124378435