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

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

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啦。



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


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


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


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

相关文章
|
20小时前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
1月前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找等具体详解步骤以及举例说明
|
1月前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
82 3
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找习题精讲等具体详解步骤以及举例说明
|
2月前
|
存储
数据结构(顺序表)
数据结构(顺序表)
31 0
|
2月前
|
存储 算法
【数据结构】新篇章 -- 顺序表
【数据结构】新篇章 -- 顺序表
23 0
|
2月前
|
存储 测试技术
探索数据结构:顺序表的实现与应用
探索数据结构:顺序表的实现与应用
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
207 9
|
1月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
35 1
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
53 5