自学内容网 自学内容网

顺序表的实现

一.线性表

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

二.顺序表

1. 概念及结构

定义:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数

组存储,在数组上完成数据的增删查改。

2.分类与特点

顺序表一般可以分为:

1). 静态顺序表:通过使用定长数组来存储数据。

2). 动态顺序表:通过使用动态开辟的数组存储数据。

静态顺序表:

优点:

1. 支持[]直接访问,查找元素方便。

2. 易于实现,操作简单。

缺点:

1. 数组的大小受到限制,必须提前规定好,无法进行扩容操作。

2. 在进行增删查改操作时需要频繁进行元素的移动,时间消耗与算法效率较为低下。

因此,一般在实际应用时,我们都采取动态顺序表而非静态顺序表。

三.具体接口实现

具体相关接口如下:

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

typedef int sldatatype;
typedef struct Seqlist
{
sldatatype* arr;
sldatatype size;
sldatatype capacity;
} sl;
void slinit(sl*);//初始化
void sldestroy(sl*);//销毁

void slprint(sl*);//打印
void checkcapacity(sl*);//判断空间是否足够
void slpushback(sl*, sldatatype);//尾插
void slpushfront(sl*, sldatatype);//头插

void slpopfront(sl*);//头删
void slpopback(sl*);//尾删

//在指定位置插入和删除数据
void slinsert(sl*, sldatatype, int);
void slfrase(sl*, int);


//查找指定数据
 int slfind(sl*, sldatatype);

首先我们定义一个结构体,里面包含一个int*的指针来充当数组存储数据

size用来存储数组元素个数。

capacity用来表示当前数组容积。

1.顺序表的初始化与销毁

void slinit(sl* ps)
{
    assert(ps);
ps->arr = NULL;
ps->capacity = ps->size = 0;
}

void sldestroy(sl*ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
ps->arr = NULL;
}
ps->capacity = ps->size = 0;
}

分析:

1.首先暴力断言保证程序的安全性,避免传入空指针。

2.初始化时将数组置为空,并将数组的size和capacity置为0。

3. 销毁时需要注意由于每一个空间都是动态申请的,我们需要依次释放,避免引起内存泄漏。

4. 另外不可以直接释放数组arr,否则会导致后续元素无法访问。

为了后续便于测试,我们先构建一个打印函数。

void slprint(sl* ps)
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
}

2.顺序表的插入

注意:

1.每次插入前都需要检查当前数组容积是否足以继续容纳!

2.在扩容时不可以用arr直接接收开辟的空间,否则一旦开辟失败,原先的arr也会被置为空,会导致后续也无法访问!

扩容操作检查如下:

void slcheckcapacity(sl* ps)
{
assert(ps);
if (ps->capacity == ps->size)
{
//增容
//若capacity为0,给个默认值,否则乘以2
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
sldatatype* tmp = (sldatatype*)realloc(ps->arr, newcapacity * sizeof(sldatatype));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = tmp;
ps->capacity = newcapacity;
}

}

尾插:

void slpushback(sl* ps, sldatatype x)
{
assert(ps);
slcheckcapacity(ps);
ps->arr[ps->size++] = x;
}

在确认可以继续插入后,直接将数据置于数组末尾并更新元素个数即可。

头插:

void slpushfront(sl* ps, sldatatype x)
{
assert(ps);
slcheckcapacity(ps);
for(int i=ps->size;i>0;i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}

与尾插同理,头插即为将元素插入数组头部。与尾插不同的是,头插需要首先将元素依次向后移动有一个位置,之后再将元素插入数组首节点处。

在指定位置插入:

void slinsert(sl* ps, sldatatype x, int pos)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
slcheckcapacity(ps);
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}

此处的pos即为要插入节点的下标,插入的元素位于该节点之后。同样也需要移动元素。

3.顺序表的删除

注意:删除数据一定要检查数组元素个数是否为0,即size是否等于0!

尾删:

void slpopback(sl* ps)
{
assert(ps && ps->size);
ps->size--;
//ps->arr[ps->size] = 0;多余了,没有必要
}

头删:

void slpopfront(sl* ps)
{
assert(ps && ps->size);
for (int i = 0; i <ps->size-1 ; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}

在删除后需要将size--。

在指定位置删除:

void slfrase(sl* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);//包含了顺序表不为空的限定条件
for (int i = pos; i <ps->size-1 ; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
    ps->size--;
}

该原理与在指定位置插入类似,在此不做过多阐述。

4.顺序表查找指定数据:

int slfind(sl* ps, sldatatype x)
{
assert(ps);
int i = 0;
int flag = 0;
for (i = 0; i < ps->size; i++)
{
if(ps->arr[i]==x)
{
flag = 1;
break;
}
}
if (flag)
return 1;
else
{
return -1;
}
}

直接遍历查找即可,查找成功返回1,查找失败返回-1。

也可将函数返回值设置为布尔类型,或者sl*,直接返回要查找节点的地址。

总结:以上就是顺序表的大致内容讲解,快来看我的下一篇链表吧!


 

          

 


原文地址:https://blog.csdn.net/2303_81060385/article/details/142600082

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!