顺序表(数据结构)---排队啦!(二)

简介: 顺序表(数据结构)---排队啦!(二)

4*接口函数的详细讲解


4.1初始化结构体

 记住结构体中,有三个结构成员,a是用来指向堆区数组的指针、size是用来表示数组里有多少个数据、capacity是用来表示数组能存储的数据个数。



 下面我们就不再写结构体是什么了,就是写接口函数,这样才不会占用太多的篇幅。


//初始化结构体
void SeqListInit(SL* ps)
{
  ps->a = NULL;
  ps->size = ps->capacity = 0;
}
int main()
{
    SL s1;
    SeqListInit(&s1)
}

 看到下面这个图,相信不是很理解指针和结构的读者都可以尝试理解。



4.2尾插

void SeqListPushBack(SL* ps, SLDataType x)
{
    //检查容量
    if(ps->size == ps->capacity)
    {
        //为了防止初始化capacity为0,每次乘二也为0,所以判断capacity是否为0,为零先赋值成4。
        int newcapacity = ps->capacity == 0 ? 4: capacity*2;
        //注意是SLDataType*,返回这块空间的首元素地址,是SLDataType类型。
        SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity*sizeof(SLDataType));
        //判断realloc成不成功,不成功realloc返回NULL
        if(tmp == NULL)
        {
            printf(realloc 失败\n);
            //系统函数exit,执行到exit整个程序直接结束,-1是一个错误码而已。
            exit(-1);
        }
        //将新空间的地址交给a管理
        ps->a = tmp;
        //更新capacity的容量
        ps->capacity = newcapacity;
    }
    //尾部加元素,size是数组最后一个数据下一个位置的下标
    ps->a[ps->size] = x;
    size++;
}


 尾插其实不难的:大家看看下面的图吧



  需要额外关注的一点是:每次插入数据,数据个数增加,我们需要考虑size会不会等于capacity,一旦等于就扩容,我们前面的检查容量就是做这个事情。


4.3打印数据

 既然通过尾插插入了一些数据,我们需要看一看有没有成功,把数组里的内容打印出来看看。

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

 打印也很简单~,用VS编译器测试一下出来看吧!



4.4用完后销毁创建的堆空间

void SeqListDestory(SL* ps)
{
    free(ps->a);
    ps->a = NULL;
    //可以连续赋值
    ps->size = ps->capacity = 0;
}


 补充:如果一个堆区空间没有被释放,那么将会引起内存泄漏,导致程序的内存空间越来越少,因为没有释放的堆区空间既不能被我们使用(指向该处的指针已被销毁),操作系统也没法回收。所以,只要动态开辟了空间的,最后要释放掉。


4.5 尾删

void SeqListPopBack(SL* ps)
{
    //不动于声的方式,当size不符合条件是,不执行就是了,
    //assert是只要有触碰的倾向,直接报错,可以看下面另一种方式的解释。
    if(ps->size > 0)
    {
         ps->size--;    
    }
}

 尾删的时候,让size--就可以了。因为size是标识着顺序表的有效数据个数的,当size减一的时候,顺序表的最后一个数据就不再是有效数据了,顺序表访问不到了。


 当没有数据的时候,size不要继续往后减了,因为这样size表示的就是数组外的下标了,此时很容易造成非法访问的错误,所以加上size>0的限制条件。比如当size为1的时候,尾删1个,此时,size符合条件进入if并成功将size减成了0,再次调用的时候,不符合条件,防止将size减减。


 下面是另一种方式:

#include <assert.h>
void SeqListPopBcak(SL* ps)
{
    //断言,如果条件为真则相安无事,如果条件为假,直接挂断程序,并报出错误。
    assert(ps->size > 0);
    ps->size--;
}

4.6头插

 在数组的第一个位置插入一个元素,为了实现这个做法,我们需要把原先所有的数据往后挪一个位置。


 不管是头插还是尾插,只要是插入,就得保证插入数据后不会超出容量,也就是在头插实现部分也要使用检查容量的代码,因此我们可以把检查容量的代码封装成一个函数,调用就可以了。



 接下来看到头插的实现部分:

void SeqListPushFront(SL* ps, SLDataType x)
{
    SeqListCheckCapacity(ps);//检查容量的接口函数
    int end = ps->size-1;
    while(end>=0)
    {
        ps->a[end+1] = ps->a[end];
        end--;
    }
    ps->a[0] = x;
    ps->size++;
}
//用for循环实现移动数据
int end = ps->size-1;
for(int i = end; i >= 0; i--)
{
    ps->a[i+1] = ps->[i];
}
ps->a[0] = x;
ps->size++;


 将检查容量,决定需不需要扩容的代码块分装成函数,很方便。




4.7头删

 头删其实就是将第一个数据后的所有元素向前移动一个数据位置。

void SeqListPopFront(SL* ps)
{
    assert(ps->size > 0);
    int begin = 1;
    while(begin < ps->size)
    {
        ps->a[begin-1] = ps->a[begin];
        ++begin;
    }
    ps->size--;
}
//用for循环实现移动数据
int begin = 1;
for(int i = begin; i < ps->size; i++)
{
    ps->a[i-1] = ps->a[i];
}
ps->size--;
//也可以是
int begin = 0;
for(int i = begin; i < ps->size-1; i++)
{
    ps->a[i] = ps->a[i+1];
}


4.8查找

 查找是在数组里查找到一个数据,找到了返回数据的下标,找不到返回-1。

int SeqList(SL* ps, SLDataType x)
{
    for(int i = 0; i < ps->size; i++)
    {
        if(ps->a[i] == x)
        {
            return i;
        }
    }
    return -1;
}

4.9任意位置插入

 对于任意位置插入,有一定的插入范围限制;一个是不能插入到超过size下标的地方,可以等于。插入在以size为下标的地方,这相当与尾插;另一个是不能在小于0的下标处插入数据;

void SeqListInsert(SL* ps, int pos, SLDataType x)//pos是要插入的下标
{
    //温柔的方式
    /*
    if(pos > ps->size || pos < 0)
    {
        printf("下标pos不能插入数据\n");
        return;
    }
    */
    //暴力的方式
    assert(pos <= ps->size && pos>=0);
    SeqListCheckCapacity(ps);
    int end = ps->size-1;
    //包括要插入的位置的数据也给往后挪,也就是end=pos进入循环
    while(end>=pos)
    {
        ps->a[end+1] = ps->a[end];
        --end;
    }
    ps->a[pos] = x;
    ps->size++;
}
//使用for循环挪动数据
int end = ps->size-1;
for(int i = end; i >= pos; i--)
{
    ps->a[i+1] = ps->a[i];
}


 如果想在某个数据位置上插入一个新的数据,我们可以使用SeqListFind计算出相应的下标,带进任意插入的接口函数中就可以了。



 改变其它的函数:


 改变头插


 既然我们现在已经实现了可以再任意位置插入,那头插就是任意插入的一个特殊情况,我们可以直接锁定插入的位置pos为0,调用函数SeqListInsert(&s1, 0, 数据);尾插也一样;

void SeqListPushFront(SL* ps, SLDataType x)
{
    //一句代码搞定头插
    SeqListInsert(ps, 0, x);

 尾插如下:

void SeqListPushBack(SL* ps, SLDataType x)
{
    SeqListInsert(ps, ps->size, x);
}

4.10任意位置删除

 任意删除的位置的范围限制是:pos的位置不能小于下标0、pos的位置不能大于size。这里不像插入数据一样可以等于size,因为size为下标处没有数据。

void SeqListErase(SL* ps, int pos)
{
    assert(ps->size > 0);//保证有数字可以删除
    assert(pos >= 0 && pos < ps->size);
    int begin = pos + 1;
    while(begin < ps->size)
    {
        ps->a[begin-1] = ps->a[begin];
        ++begin;
    }
    ps->size--;
}
//用for循环移动数据
int begin = pos;
for(int i = begin; i < ps->size-1; i++)
{
    ps->a[i] = ps->a[i+1];
}
ps->size--



 同上:学完任意位置删除元素,我们可以对头删和尾删给改一下,它们都是是任意位置删除的特殊情况。


 头删:

void SeqListPopFront(SL* ps)
{
    SeqListEras(ps, 0);
}

 尾删:

void SeqListPopBack(SL* ps)
{
    SeqLisErase(ps, ps->size-1);
}

 尾部删除就是最后一个数据,下标自然是size-1啦。



 好啦,到这里,顺序表的内容就讲完啦,更新不易,求波点赞吧~


结语:希望读者读完能有所收获!对数据结构有进一步的认识!✔


 读者对本文不理解的地方,或是发现文章内容上有误等,请在下方评论留言告诉博主哟~,也可以对博主提出一些文章改进的建议,感激不尽!最后的最后!


 ❤求点赞,求关注,你的点赞是我更新的动力,一起进步吧。

相关文章
|
3天前
|
存储 Java 程序员
【数据结构】初识集合&深入剖析顺序表(Arraylist)
Java集合框架主要由接口、实现类及迭代器组成,包括Collection和Map两大类。Collection涵盖List(有序、可重复)、Set(无序、不可重复),Map则由键值对构成。集合通过接口定义基本操作,具体实现由各类如ArrayList、HashSet等提供。迭代器允许遍历集合而不暴露其实现细节。List系列集合元素有序且可重复,Set系列元素无序且不可重复。集合遍历可通过迭代器、增强for循环、普通for循环及Lambda表达式实现,各有适用场景。其中ArrayList实现了动态数组功能,可根据需求自动调整大小。
23 11
|
11天前
|
存储 C语言 C++
数据结构基础详解(C语言) 顺序表:顺序表静态分配和动态分配增删改查基本操作的基本介绍及c语言代码实现
本文介绍了顺序表的定义及其在C/C++中的实现方法。顺序表通过连续存储空间实现线性表,使逻辑上相邻的元素在物理位置上也相邻。文章详细描述了静态分配与动态分配两种方式下的顺序表定义、初始化、插入、删除、查找等基本操作,并提供了具体代码示例。静态分配方式下顺序表的长度固定,而动态分配则可根据需求调整大小。此外,还总结了顺序表的优点,如随机访问效率高、存储密度大,以及缺点,如扩展不便和插入删除操作成本高等特点。
|
10天前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
1月前
|
存储 算法
【数据结构与算法】顺序表
【数据结构与算法】顺序表
13 0
【数据结构与算法】顺序表
|
1月前
|
存储 算法
【初阶数据结构篇】顺序表和链表算法题
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
|
1月前
|
存储 测试技术
【初阶数据结构篇】顺序表的实现(赋源码)
线性表(linearlist)是n个具有相同特性的数据元素的有限序列。
|
1月前
|
存储 编译器
【数据结构】顺序表(长期维护)
【数据结构】顺序表(长期维护)
|
8天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
11天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
|
12天前
|
Java
【数据结构】栈和队列的深度探索,从实现到应用详解
本文介绍了栈和队列这两种数据结构。栈是一种后进先出(LIFO)的数据结构,元素只能从栈顶进行插入和删除。栈的基本操作包括压栈、出栈、获取栈顶元素、判断是否为空及获取栈的大小。栈可以通过数组或链表实现,并可用于将递归转化为循环。队列则是一种先进先出(FIFO)的数据结构,元素只能从队尾插入,从队首移除。队列的基本操作包括入队、出队、获取队首元素、判断是否为空及获取队列大小。队列可通过双向链表或数组实现。此外,双端队列(Deque)支持两端插入和删除元素,提供了更丰富的操作。
14 0
【数据结构】栈和队列的深度探索,从实现到应用详解