【数据结构初阶】--- 序次表

打印 上一主题 下一主题

主题 676|帖子 676|积分 2028

序次表,好像学C语言时从来没听过,现实上就是给数组穿了层衣服,本质是一模一样的。
这里的序次表现实是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增编削查插入,这些基本操作其实平常就会在利用数组的时候用到。
那么,今天就带你深度剖析线性表(数组)的底层原理
这节知识只是个开胃菜,但也属于要必备的知识,所以各位同砚不要松弛哦
引入

序次表有静态的也有动态的


  • 静态序次表,静,无非就是初始化这个静态表后,这个静态表的空间就不能变化了,例如:
  1. struct SeqList
  2. * {
  3. *                 int arr[20];
  4. *             int size;
  5. * };
复制代码


  • 当初始化线性表后,这个数组存的数据就不能更改了
  • 那有人会想,我用个宏常量更换数组的大小,到时后想扩大或缩小,更改宏常量就行了
    #define N 20 int arr[N];
  • 这个想法不错,但也只能在线性表初始化之前更改,如果你初始化后,你正在利用线性表的功能时,内存不敷用了怎么办
那么,接下来我所编写的代码就是动态序次表,当你不敷用内存的时候,会自动给你开辟,当看到这里,如果前面【C语言进阶】— 动态内存管理,你看过,那接下来的知识轻而易举,乃至不消看我对代码的注释就可以清晰,没看过的朋友,强烈推荐,很重要!!!数据结构会经常用到这篇的知识

1. 序次表在内存中的存储方式

是一块连续的内存

2. 序次表的定义

这里是用结构体定义了一个序次表类型
  1. typedef int SeqListType;//因为顺序表中存储的数据不见得都是int型,也可能是别的类型数据,所以,想存放别的数据时只需把int换成你想存的数据的类型
  2. //动态顺序表
  3. typedef struct SeqList
  4. {
  5.         SeqListType* data;//指针指向的是SeqListType这个类型的数据
  6.         int size;//记录当前有效储存数据的个数
  7.         int capacity;//data开辟数组空间的容量
  8. }SeqList;
复制代码
初始化、烧毁、打印功能

  1. //初始化顺序表(开辟空间,赋初值)
  2. void SLInit(SeqList* ps)
  3. {
  4.         //开辟一块大小为sizeof(SeqListType) * 4的内存,并把首地址传给指针ps->data
  5.         ps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);
  6.         if (ps->data == NULL)
  7.         {
  8.                 perror("malloc");
  9.                 return;
  10.         }
  11.         ps->size = 0;
  12.         ps->capacity = 4;
  13. }
  14. //销毁空间,因为是在堆区开辟的数据,用完要用free函数释放
  15. void SLDestory(SeqList* ps)
  16. {
  17.         free(ps->data);
  18.         ps->data = NULL;
  19.         ps->size = 0;
  20.         ps->capacity = 0;
  21. }
  22. void SLPrint(SeqList* ps)
  23. {
  24.         for (int i = 0; i < ps->size; i++)
  25.         {
  26.                 printf("%d ", ps->data[i]);
  27.         }
  28.         printf("\n");
  29. }
复制代码
3. 序次表的功能实现

扩容功能,先跳过看头插法
  1. //检查容量,不足顺便扩容
  2. void CheckCapacity(SeqList* ps)
  3. {
  4.         if (ps->capacity == ps->size)
  5.         {
  6.                 //用realloc给ps->data开辟一个原来2倍的空间
  7.                 SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);
  8.                 if (new_ps != NULL)//判断返是否开辟成功,若为NULL则开辟失败
  9.                 {
  10.                         ps->data = new_ps;
  11.                         new_ps = NULL;
  12.                         ps->capacity *= 2;
  13.                 }
  14.                 else
  15.                 {
  16.                         perror("realloc");
  17.                         return;
  18.                 }
  19.         }
  20. }
复制代码
3.1 头插法、尾插法

  1. //头插法
  2. void SLPushFront(SeqList* ps, SeqListType x)
  3. {
  4.         //检查目前容量,不足就扩容
  5.         CheckCapacity(ps);
  6.         for (int i = ps->size - 1; i >= 0; i--)
  7.         {
  8.                 ps->data[i + 1] = ps->data[i];
  9.         }
  10.         ps->data[0] = x;
  11.         ps->size++;
  12. }
  13. //尾插法
  14. void SLPushBack(SeqList* ps, SeqListType x)
  15. {
  16.         CheckCapacity(ps);
  17.         ps->data[ps->size] = x;
  18.         ps->size++;
  19. }
复制代码
3.2 头删法、尾删法

  1. //头删法
  2. void SLPopFront(SeqList* ps)
  3. {
  4.         //这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针
  5.         assert(ps != NULL);
  6. //*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************
  7.         assert(ps->size > 0);
  8.         for (int i = 1; i < ps->size; i++)
  9.         {
  10.                 ps->data[i - 1] = ps->data[i];
  11.         }
  12.         ps->size--;
  13. }
  14. //尾删法
  15. void SLPopBack(SeqList* ps)
  16. {
  17.         assert(ps != NULL);
  18.         assert(ps->size > 0);
  19.         ps->size--;
  20. }
复制代码
3.3 给指定位置插入数据

  1. void SLInsert(SeqList* ps, int position, SeqListType x)
  2. {
  3.         assert(ps != NULL);
  4.         //判断给的位置是否越界,若越界,程序执行完会告诉你这里出的问题
  5.         assert(position >= 1 && position <= ps->size);
  6.         CheckCapacity(ps);
  7.         for (int i = ps->size - 1; i >= position - 1; i--)
  8.         {
  9.                 ps->data[i + 1] = ps->data[i];
  10.         }
  11.         ps->data[position - 1] = x;
  12.         ps->size++;
  13. }
复制代码
3.4 指定位置删除数据

  1. void SLErase(SeqList* ps, int position)
  2. {
  3.         assert(ps != NULL);
  4.         assert(position >= 1 && position <= ps->size);
  5.         assert(ps->size > 0);
  6.         for (int i = position; i < ps->size; i++)
  7.         {
  8.                 ps->data[i - 1] = ps->data[i];
  9.         }
  10.         ps->size--;
  11. }
复制代码
3.5 查找指定命据

  1. int SLFind(SeqList* ps, SeqListType x)
  2. {
  3.         assert(ps != NULL);
  4.         for (int i = 0; i < ps->size; i++)
  5.         {
  6.                 if (ps->data[i] == x)
  7.                         return i;
  8.         }
  9.         return -1;
  10. }
复制代码
4.序次表的优缺点

4.1 长处

由于它跟数组一样可以举行下表访问,所以当你要查询数据时时间复杂度为O(1),是常数级,访问速度很快很方便,这与它的内存是一片连续的内存密切相关,由于当你访问数组下标为i的数据时,arr本质是*(arr+i),首元素地点+i解引用
4.3 缺点

也正因它的内存是连续的,所以当你要删除、插入数据时必须要移动背面的数据,时间复杂度是O(N)级别的。
5 完整代码

5.1 头文件 SeqList.h 中的代码

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. typedef int SeqListType;
  6. //动态顺序表
  7. typedef struct SeqList
  8. {
  9.         SeqListType* data;
  10.         int size;
  11.         int capacity;
  12. }SeqList;
  13. //初始化顺序表
  14. void SLInit(SeqList* ps);
  15. //销毁顺序表
  16. void SLDestory(SeqList* ps);
  17. //打印顺序表
  18. void SLPrint(SeqList* ps);
  19. //头插法插入数据
  20. void SLPushFront(SeqList* ps, SeqListType x);
  21. //尾插法
  22. void SLPushBack(SeqList* ps, SeqListType x);
  23. //头删法
  24. void SLPopFront(SeqList* ps);
  25. //尾删法
  26. void SLPopBack(SeqList* ps);
  27. //指定位置插入数据
  28. void SLInsert(SeqList* ps, int position, SeqListType x);
  29. //指定位置删除
  30. void SLErase(SeqList* ps, int position);
  31. //查找数据
  32. int SLFind(SeqList* ps, SeqListType x);
复制代码
5.2 函数实现的文件 SeqList.c

  1. #define        _CRT_SECURE_NO_WARNINGS#include"SeqList.h"void SLInit(SeqList* ps){        ps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);        if (ps->data == NULL)        {                perror("malloc");                return;        }        ps->size = 0;        ps->capacity = 4;}void SLDestory(SeqList* ps){        free(ps->data);        ps->data = NULL;        ps->size = 0;        ps->capacity = 0;}void SLPrint(SeqList* ps){        for (int i = 0; i < ps->size; i++)        {                printf("%d ", ps->data[i]);        }        printf("\n");}//检查容量,不足顺便扩容void CheckCapacity(SeqList* ps){        if (ps->capacity == ps->size)        {                //用realloc给ps->data开辟一个原来2倍的空间                SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);                if (new_ps != NULL)                {                        ps->data = new_ps;                        new_ps = NULL;                        ps->capacity *= 2;                }                else                {                        perror("realloc");                        return;                }        }}void SLPushFront(SeqList* ps, SeqListType x){        //检查现在容量,不足就扩容        CheckCapacity(ps);        for (int i = ps->size - 1; i >= 0; i--)        {                ps->data[i + 1] = ps->data[i];        }        ps->data[0] = x;        ps->size++;}//尾插法void SLPushBack(SeqList* ps, SeqListType x){        CheckCapacity(ps);        ps->data[ps->size] = x;        ps->size++;}void SLPopFront(SeqList* ps){        //这里断言的缘故起因是,如果ps是空指针,for循环条件判断就会访问空指针        assert(ps != NULL);//*****************当size==0时,也会实行ps->size--,后续大概会造成越界访问***************        assert(ps->size > 0);        for (int i = 1; i < ps->size; i++)        {                ps->data[i - 1] = ps->data[i];        }        ps->size--;}//尾删法void SLPopBack(SeqList* ps){        assert(ps != NULL);        assert(ps->size > 0);        ps->size--;}void SLInsert(SeqList* ps, int position, SeqListType x){        assert(ps != NULL);        assert(position >= 1 && position <= ps->size);        CheckCapacity(ps);        for (int i = ps->size - 1; i >= position - 1; i--)        {                ps->data[i + 1] = ps->data[i];        }        ps->data[position - 1] = x;        ps->size++;}void SLErase(SeqList* ps, int position)
  2. {
  3.         assert(ps != NULL);
  4.         assert(position >= 1 && position <= ps->size);
  5.         assert(ps->size > 0);
  6.         for (int i = position; i < ps->size; i++)
  7.         {
  8.                 ps->data[i - 1] = ps->data[i];
  9.         }
  10.         ps->size--;
  11. }
  12. int SLFind(SeqList* ps, SeqListType x)
  13. {
  14.         assert(ps != NULL);
  15.         for (int i = 0; i < ps->size; i++)
  16.         {
  17.                 if (ps->data[i] == x)
  18.                         return i;
  19.         }
  20.         return -1;
  21. }
复制代码
这两套代码不能直接运行,主函数int main(){}中的内容自己写,就剩调用函数了,让自己动动手敲几行代码吧,好的步伐员一定是须要实践的,只管序次表这一节相对不难,但里面有些边界值的判断,只有你亲手敲代码的时候才能真正进入思索,加油各位!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户云卷云舒

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表