数据结构漫游记:顺序表的实现C/C++

 

fdc3d53809cd4ff9be4de86427bcd614.gif

引入

我们来学学在数据结构中学到的第一个也是最简单最常用结构,线性表....中的顺序表吧!先回顾一下线性表,线性表(list):零个或多个元素的有限序列。这里注意无论是顺序表还是链表,他们在逻辑上都是线性的,但在物理上却不一定哦,今天我们要介绍的顺序表,无论是在逻辑上还是物理上都是线性的.

话不多说让我们回归主题,顺序表就可以理解为下面这个图。

bb261e1c006b431fb22378a111862a0d.jpeg

大家有没有觉得这个图怎么这么像数组呢,没错我们可以利用数组来实现它。那我们有两个选择,要么用很大的数组来当线性表,这就是一个静态的顺序表了,还有一种做法,在c语言中我们可以利用malloc或者ralloc来申请空间来实现,具体要使用那个我们之后慢慢说。这两种方法各自有各自的优点,我之前在我的链表的博客中已经提到了,这里就不过多介绍了。

静态顺序表

先来讲讲静态顺序表吧!这里我就用C++来实现咯。

首先我们得考虑一件事情,我们开辟的数组应该放在main函数外面成为全局变量,还是放在main里面作为局部变量呢?

如果你有幸看过鹏哥的函数栈帧,那就应该能很好理解应该放在哪里呢,没看过也没关系,让我来简单介绍一下,全局变量存放在静态数据区,局部变量放在栈区中。很明显静态区里面的空间要大很多。

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
//定义一个非常大的数组
int arr[N]
//用n来记录元素个数
int n;

定义好了这玩意,又要进行我们的增删查改了,这个就是数组的插入等问题,应该对于很多有一定基础同学来说是很简单了,我这里就不一一赘述了。

#include<iostream>
using namespace std;
const int N = 1e5 + 10;

int a[N];//一个足够大的数组

int n;//数组的元素个数

//打印
void print()
{
	for (int i = 0; i < n; i++)
		cout << a[i] << " ";
	cout << endl;
}
//尾插
void push_back(int d)
{
	a[n] = d;
	n++;
}
//头插
void push_front(int d)
{
	for (int i = n; i > 0; i--)
		a[i] = a[i - 1];
	a[0] = d;
	n++;
}
//头删
void pop_front()
{
	for (int i = 0; i < n; i++)
		a[i] = a[i + 1];
	n--;
}
//尾删
void pop_back()
{
	n--;
}
//查找
int find(int d)
{
	for (int i = 0; i < n; i++)
		if (a[i] == d)
			return i;
	return -1;
}
//删除指定元素
void erase(int pos)
{
	for (int i = pos; i < n; i++)
		a[i] = a[i + 1];
	n--;
}
//任意位置插入
void insert(int pos ,int d)
{
	for (int i = n; i > pos; i--)
		a[i] = a[i - 1];
	a[pos] = d;
	n++;
}

int main()
{
	
	return 0;
}

动态顺序表

接下来就讲讲重头戏吧,用C语言来实现动态顺序表。

首先我们得创建一个结构体来实现.

#include<stdio.h>

typedef int SLDateType;

typedef struct SeqList
{
	SLDateType* a;//存储元素
	int size;//元素个数
	int capacity;//容积
}SeqList;

先来看看我们要实现的函数


// 对数据的管理:增删查改 
void SeqListInit(SeqList* ps);
void SeqListDestroy(SeqList* ps);

void SeqListPrint(SeqList* ps);
void SeqListPushBack(SeqList* ps, SLDateType x);
void SeqListPushFront(SeqList* ps, SLDateType x);
void SeqListPopFront(SeqList* ps);
void SeqListPopBack(SeqList* ps);

// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);

初始化

我们单单创建结构体时,如果我们不对它进行初始化,里面的size和capacity都是随机值。所以进行初始化是很有必要的。初始化就将这两个值以及a指针指向NULL

void SeqListUnit(SeqList * ps)
{
    ps->a = NULL;
    ps->capacity = ps->size = 0;

}

 

尾插

要实现尾插,其实就是在数组的末尾插入新数据,然后将size加1。注意此时size是始终指向空元素的即下一个元素,我可能说的有点抽象那让我们看看图吧!

b491551dd20a40dc8190da35303e05ff.jpeg

其实单单这个几行代码就能实现

void SeqListPushBack(SeqLis* ps,SLDateType x)
{
    ps->a[size++] = x; 
}

但当我们的顺序表中没有元素时我们需要开辟空间来存储数据,就要考虑使用malloc,realloc函数了,realloc其实就是用来扩容的函数。一般我们扩容都是阔原来的内存的2倍,

int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;

这样用三目操作符能简单实现这个问题。

SeqList* tmp = (SeqList*)realloc(ps->a, newcapacity * sizeof(SLDateType));

这里要用tmp指针先暂时接收创建出来的内存空间,以防realloc申请失败,会造成内存泄漏,以后在公司中出现这种问题,造成的后果可不是你能想象的哦!

我们实现插入时可以都要进行扩容,所以我们把检查是否扩容分装成一个函数如下图

void SeqlistCheckCapacity(SeqList* ps)
{
	if (ps->capacity == ps->size)
	{
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SeqList* tmp = (SeqList*)realloc(ps->a, newcapacity * sizeof(SLDateType));
		if (tmp == NULL)
		{
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	
}

所以尾插就很好的实现了

void SeqListPushBack(SeqList* ps, SLDateType x)
{
	assert(ps);
	SeqlistCheckCapacity(ps);
	ps->a[ps->size++] = x;
}

头插

头插相对于尾插显得要麻烦一点,我们得把所有元素向后移动一位再将要插入的元素插入,这里要十分注意,就是我们应该要从后往前移动。

91254b9cac09465f82cca04cabeebcac.png

任意位置插入

这个如果实现了头插就很容易了,将0改成我们要插入的位置的小标,我这里也就不啰嗦了哈。

0553351aef884aeba3485da20e5b9539.png

尾删

尾删应该算是很简单的吧,只需要将元素个数减减就可以了。

0a526452588c429b8fd9668306ef936d.png

头插

头插也需要将元素挪一挪。

02fd1cb557584a1a9fc5ca8fa6edfd3c.png

 

删除指定元素

这对于你来说也是小菜一碟了。

不过要注意的是要检查数组的个数不能等于0

d5d608106d764efdaedbd42a41deadd6.png

 

查找

之前都会了那这个就是小意思了。

9384a462e86b457783a09dc1643bcdbd.png

 

销毁

销毁数组创建的内存也是一个很有必要的事情,我们不能只借不还啊,申请了内存,也要记得还回来。

4830706f56124593a6250ad7e5a3601f.png

 

完结撒花!

 

9eb8cc85025b43ea8a95cf4b3ff23307.gif

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值