数据结构篇一:顺序表解析及实现

前言

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

概念及结构

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

各种功能的解析及实现

顺序表的创建

  我们需要先创建几个变量来存放我们所需要的数据,首先肯定需要一个数组,这里为了提高空间利用率,我采取了动态开辟的方式来实现顺序表,因此我们还需要一个变量sz在标志我们已经存放了多少个数据,以及另一个变量来标志当前的容量capacity。(因为这里选择实现的方式并不是满了之后只增加一个空间,是一次性增加多个空间,所以需要记录当前的容量来与sz进行比较,看空间是否已经存放满了)

typedef struct SeqList
{
    
    
	SLDataType* data;
	int sz;
	int capacity;
}SL;

初始化

  将所有数据初始化为空,以便后续的操作。

void InitSeqList(SL* ps)
{
    
    
	ps->data = NULL;
	ps->sz = 0;
	ps->capacity = 0;
}

增容

  刚开始可使用的空间为0,所以我们需要先进行开辟空间来让我们能够存放数据。

void AddCapacity(SL* ps)
{
    
    
	//增容
	if (ps->sz == ps->capacity)
	{
    
    
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* pc = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * newcapacity);
		if (pc == NULL)
		{
    
    
			perror("SeqListPushBack");
		}
		ps->data = pc;
		ps->capacity = newcapacity;
	}
}

  当ps->sz == ps->capacity时,也就是当前存储的元素个数等于了当前的最大容量,也就意味着已经空间已经满了,所以就进行开辟空间。至于SLDataType是元素的类型,我这里是将int重新定义为它了,这样方便我们以后修改元素的类型,能做到一改全改的效果。

尾插

  在线性表的结尾新插入一个元素。

void SeqListPushBack(SL* ps, SLDataType x)
{
    
    
	AddCapacity(ps);
	ps->data[ps->sz] = x;
	ps->sz++;
}

  因为需要新加入一个元素,而我们并不知道当前是否线性表已经存满了,所以刚进来需要先判断需不需要增容。我们需要要将所需要插入的元素放到结尾,同时sz(记录元素个数的变量)再加1就可以了。

尾删

  这里我写两种判断方式,都可以使用,如果要使用assert记得加上头文件<assert.h>哦。这里是为了避免顺序表已经为空,而我们还要继续删除的错误操作。

void SeqListPopback(SL* ps)
{
    
    
	//assert(ps->sz > 0);
	if (ps->sz > 0)
	{
    
    
		ps->sz--;
	}
}

  这里并不需要将它删除,我们我们在访问这里元素的时候,是通过下标来进行访问的。如最后一个元素的下标为ps->sz-1,我们只需要使sz-1,就可以完成记录元素个数的变量sz减1的同时,还可以使它访问不到最后一个元素。如图
在这里插入图片描述

打印

  来帮助我们在屏幕上显示我们当前存放数据的内容。

void SeqListPrint(SL* ps)
{
    
    
	int i = 0;
	for (i = 0; i < ps->sz; i++)
	{
    
    
		printf("%d ", ps->data[i]);
	}
	printf("\n");
}

  从头到尾挨个输出即可。

头插

  在顺序表的开头插入一个元素。

void SeqListPushFront(SL* ps, SLDataType x)
{
    
    
	AddCapacity(ps);
	int end = ps->sz - 1;
	while (end >= 0)
	{
    
    
		ps->data[end + 1] = ps->data[end];
		end--;
	}
	ps->data[0] = x;
	ps->sz++;
}

  同样的,这也是新加入了一个元素,我们也需要判断是否需要进行增容。我们要在开头插入一个元素,那么因为顺序表的性质我们必须先将全部元素向后移一个元素,来腾出第一个元素空间。我们只需要从最后开始,一个一个向后移一个空间就可以了。

头删

  删除第一个元素。


void SeqListPopFront(SL* ps)
{
    
    
	if (ps->sz > 0)
	{
    
    
		int front = 1;
		while (front < ps->sz)
		{
    
    
			ps->data[front - 1] = ps->data[front];
			front++;
		}
		ps->sz--;
	}
}

  因为是删除,所以我们需要判断顺序表是否为空,为空的话就不进行删除了。这个只需要从第二个元素开始,一个一个向前覆盖就可以了,第二个元素覆盖第一个元素,第三个元素覆盖第二个元素……

查找

  找到某一元素的下标,如果没有,就显示不存在。

int SeqListFind(SL* ps, SLDataType x)
{
    
    
	int front = 0;
	while (front < ps->sz)
	{
    
    
		if (ps->data[front] == x)
		{
    
    
			return front;
		}
		front++;
	}
	printf("元素不存在\n");
	return -1;
}

  从第一个元素开始,一个一个进行比较,相等了就返回下标,一直到结束都没有找到相等的显示不存在。

在pos位置插入

  在某一位置插入一个元素,这里实现的是在pos位置前插入。

void SeqListInsert(SL* ps, int pos, SLDataType x)
{
    
    
	AddCapacity(ps);//增容
	assert(pos > 0 && pos <= ps->sz);//判断用户所输入的位置是否越界
	pos -= 1;               //转化为对应的下标
	int end = ps->sz - 1;  
	while (pos <= end)   //一直将下标为pos位置的元素挪完
	{
    
    
		ps->data[end + 1] = ps->data[end];   //将元素从后一个一个往后挪
		end--;
	}
	ps->data[pos] = x;
	ps->sz++;
}

  因为数组是从0开始,我们要在第2个位置插入元素,实际是存放在ps->data[1]当中的。代码中有详细介绍,这里就不多说了。

在pos位置删除

  删除某一位置的元素。

void SeqListErase(SL* ps, int pos)
{
    
    
	int end = pos - 1;      //将end与下标相对应,指向第pos个元素的下标
	if (pos > ps->sz)
	{
    
    
		printf("该位置没有元素\n");
	}
	if (pos > 0 && pos <= ps->sz)   //保证顺序表里要有元素,并且删除的元素位置必须小于最后一个元素的位置
	{
    
    
		while (end < ps->sz - 1)      //不断往前覆盖,当然也可以一开始不转化为下标,pos指向的是对应元素的下一个元素,直接向前覆盖也可以
		{
    
    
			ps->data[end] = ps->data[end + 1];
			end++;
		}
		ps->sz--;
	}
}

  上面代码我也写了详细的注释,这里也就不多讲了,有什么问题可以私信或者评论区留言。

销毁

  动态开辟的空间在不使用的时候都需要进行释放,防止出现内存泄漏。

void SeqListDestory(SL* ps)
{
    
    
	free(ps->data);
	ps->data = NULL;
	ps->sz = 0;
	ps->capacity = 0;
}

  这个也没什么可说的,大家看看就明白了,后面我会放上全部的代码,分为了三个文件,一个是进行结构体的定义和函数的声明,一个是函数的实现,一个是来进行测试函数功能有没有问题。

代码实现

SeqList.h

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#define SLDataType int
//顺序表的创建
typedef struct SeqList
{
    
    
	SLDataType* data;
	int sz;
	int capacity;
}SL;

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

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

//尾插
void SeqListPushBack(SL* ps, SLDataType x);

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

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

//头插
void SeqListPushFront(SL* ps, SLDataType x);

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

//查找
int SeqListFind(SL* ps, SLDataType x);

//在pos位置插入
void SeqListInsert(SL* ps, int pos, SLDataType x);

//在pos位置删除
void SeqListErase(SL* ps, int pos);

//销毁
void SeqListDestory(SL* ps);

SeqList.c

#include"SeqList.h"
void InitSeqList(SL* ps)
{
    
    
	ps->data = NULL;
	ps->sz = 0;
	ps->capacity = 0;
}

void AddCapacity(SL* ps)
{
    
    
	//增容
	if (ps->sz == ps->capacity)
	{
    
    
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* pc = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * newcapacity);
		if (pc == NULL)
		{
    
    
			perror("SeqListPushBack");
		}
		ps->data = pc;
		ps->capacity = newcapacity;
	}
}

void SeqListPushBack(SL* ps, SLDataType x)
{
    
    
	AddCapacity(ps);
	ps->data[ps->sz] = x;
	ps->sz++;
}

void SeqListPopback(SL* ps)
{
    
    
	//assert(ps->sz > 0);
	if (ps->sz > 0)
	{
    
    
		ps->sz--;
	}
}

void SeqListPrint(SL* ps)
{
    
    
	int i = 0;
	for (i = 0; i < ps->sz; i++)
	{
    
    
		printf("%d ", ps->data[i]);
	}
	printf("\n");
}

void SeqListPushFront(SL* ps, SLDataType x)
{
    
    
	AddCapacity(ps);
	int end = ps->sz - 1;
	while (end >= 0)
	{
    
    
		ps->data[end + 1] = ps->data[end];
		end--;
	}
	ps->data[0] = x;
	ps->sz++;
}

void SeqListPopFront(SL* ps)
{
    
    
	if (ps->sz > 0)
	{
    
    
		int front = 1;
		while (front < ps->sz)
		{
    
    
			ps->data[front - 1] = ps->data[front];
			front++;
		}
		ps->sz--;
	}
}

int SeqListFind(SL* ps, SLDataType x)
{
    
    
	int front = 0;
	while (front < ps->sz)
	{
    
    
		if (ps->data[front] == x)
		{
    
    
			return front;
		}
		front++;
	}
	printf("元素不存在\n");
	return -1;
}

void SeqListInsert(SL* ps, int pos, SLDataType x)
{
    
    
	AddCapacity(ps);
	assert(pos > 0 && pos <= ps->sz);
	pos -= 1;               //转化为对应的下标
	int end = ps->sz - 1;  
	while (pos <= end)   //一直将下标为pos位置的元素挪完
	{
    
    
		ps->data[end + 1] = ps->data[end];   //将元素从后一个一个往后挪
		end--;
	}
	ps->data[pos] = x;
	ps->sz++;
}

void SeqListErase(SL* ps, int pos)
{
    
    
	int end = pos - 1;      //将end与下标相对应,指向第pos个元素的下标
	if (pos > ps->sz)
	{
    
    
		printf("该位置没有元素\n");
	}
	if (pos > 0 && pos <= ps->sz)   //保证顺序表里要有元素,并且删除的元素位置必须小于最后一个元素的位置
	{
    
    
		while (end < ps->sz - 1)      //不断往前覆盖,当然也可以一开始不转化为下标,pos指向的是对应元素的下一个元素,直接向前覆盖也可以
		{
    
    
			ps->data[end] = ps->data[end + 1];
			end++;
		}
		ps->sz--;
	}
}

void SeqListDestory(SL* ps)
{
    
    
	free(ps->data);
	ps->data = NULL;
	ps->sz = 0;
	ps->capacity = 0;
}

test.c

#include"SeqList.h"
void test1()
{
    
    
	//测试功能
	SL s1;
	InitSeqList(&s1);
	SeqListPushBack(&s1, 1); //测试:尾插
	SeqListPushBack(&s1, 2);
	SeqListPushBack(&s1, 3);
	SeqListPrint(&s1);

	SeqListPopback(&s1);//测试:尾删及顺序表为空时,继续尾删会不会报错
	SeqListPopback(&s1);
	SeqListPopback(&s1);
	SeqListPopback(&s1);

	SeqListPushBack(&s1, 1);//测试:头插
	SeqListPushFront(&s1, 2);
	SeqListPushFront(&s1, 3);
	SeqListPrint(&s1);

	SeqListPopFront(&s1);  //测试:头删

	int ret = SeqListFind(&s1, 1);  //测试:查找
	if (ret != -1)
	{
    
    
		printf("找到了,下标为:>%d\n", ret);
	}

	SeqListInsert(&s1, 1, 5);//测试:在1位置插入元素5
	SeqListPrint(&s1);

	SeqListErase(&s1, 1); // 测试:删除1位置的元素
	SeqListPrint(&s1);
	SeqListErase(&s1, 4);// 测试:删除4位置的元素
	SeqListPrint(&s1);

	SeqListDestory(&s1);//销毁

}
int main()
{
    
    
	test1();
	return 0;
}

总结

  这是第一次书写数据结构的内容,此为篇一,后续我还会继续写链表以及栈,队列等的文章,希望能对大家有所帮助,觉得不错的可以点个赞嗷,谢谢大家啦!!
  这里我没有写修改的功能,可以为大家提一些建议,只需要通过查找功能找到需要修改元素的下标,然后直接进行覆盖就可以了,具体大家可以参考我之前写的通讯录有相关的实现模块,不会可以参考。
  学习的时光总是那么短暂,你们的陪伴让我更感荣幸,愿与大家共同进步,展望未来。那么本期就到此结束,让我们下期再见。

猜你喜欢

转载自blog.csdn.net/qq_62321047/article/details/129383262