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

顺序表是用一段 物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
连续存储顺序,是不能跳跃的
顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
使用定长数组存储元素
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N 定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
静态顺序表价值较低 -- 不考虑
扩容方式 -- 使用 realloc 库函数 -- 有两种扩容方式 原地扩容 与 异地扩容 后者代价大
扩容选择 -- 两倍
扩容两倍的原因
一次扩容多了,存在空间的浪费。一次扩容少了,频繁扩容,效率损失
- #pragma once //方式二次引用
- #define _CRT_SECURE_NO_DEPRECATE //VS里面的检查 -- 其他编辑器不需要
- #include
- #include
- #include
-
- //或者
- //#ifndef __SEQLIST_H__
- //#define __SEQLIST_H__
- //……
- //#endif
-
- //静态的顺序表 -- 缺陷:当不知道要存储多少数据的时候就体现出来了
- //#define N 10
- //typedef int SLDataType;
- //struct Seqlist
- //{
- // SLDataType a[N];
- // int size; //存储数据的个数
- //};
-
- //动态顺序表 -- 有两种扩容方式 原地扩容与异地扩容 后者代价大
-
- //重命名类型 -- 方便后续改变存储类型
- typedef int SLDataType;
-
- typedef struct Seqlist
- {
- SLDataType* a;
- int size; //存储数据的个数
- int capacity;//存储空间的大小
- }SL;
-
- //初始化 -- 这里要接收地址,用指针接收,利用地址来与外部产生联系
- void SLInit(SL* psl);
-
- //销毁 --
- void SLDestory(SL* psl);
-
- //顺序表的打印 -- 方便检查
- void SLPrint(SL* psl);
-
- //头插头删 尾插尾删
- //尾插
- void SLPushBack(SL* psl, SLDataType x);
-
- //头插
- void SLPushFront(SL* psl, SLDataType x);
-
- //尾删
- void SLPoPBack(SL* psl);
-
- //头删
- void SLPoPhFront(SL* psl);
-
- // 顺序表查找 -- 没找到返回-1
- int SLFind(SL* psl, SLDataType x);
-
- //顺序表在pos位置插入x
- void SLInsert(SL* psl, size_t pos, SLDataType x);
-
- // 顺序表删除pos位置的值
- void SLErase(SL* psl, size_t pos);
-
- //修改
- void SLModify(SL* psl, size_t pos, SLDataType x);
- #include "SeqList.h"
-
-
- //初始化
- void SLInit(SL* psl)
- {
- assert(psl); //自身断言不可以为空 不可以传NULL过来
- psl->a = NULL;
- psl->size = psl->capacity = 0;
- }
-
- //销毁
- void SLDestory(SL* psl)
- {
- assert(psl);
- //if (psl->a) //free自己会检查是否为NULL -- free NULL 没意义
- //{
- free(psl->a);
- psl->a = NULL;
- psl->size = psl->capacity = 0;
- //}
- }
-
-
- //检查并扩容
- void SLCheckCapacity(SL* psl)
- {
- //检查容量
- if (psl->size == psl->capacity)
- {
- int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2; //扩容两倍 -- 当为0的时候赋值4字节
- SLDataType* tmp = (SLDataType*)realloc(psl->a, newCapacity * sizeof(SLDataType)); //三种情况,原地、异地与失败 -- 为防止失败应使用临时指针
- if (tmp == NULL) // 这里开辟空间大小容易出错,必须要记得后面的* sizeof(SLDataType) 不然开辟的空间不够
- {
- perror("realloc fail");
- return;
- //exit(-1); //或者这样直接返回,更直接
- }
- psl->a = tmp;
- psl->capacity = newCapacity;
- }
- }
-
- //打印
- void SLPrint(SL* psl)
- {
- assert(psl);
- for (int i = 0; i < psl->size; i++)
- {
- printf("%d ", psl->a[i]);
- }
- printf("\n");
- }
-
- //尾插
- void SLPushBack(SL* psl, SLDataType x)
- {
-
- //assert(psl);
- //检查并扩容
- //SLCheckCapacity(psl);
-
- //psl->a[psl->size] = x;
- //psl->size++;
-
- //当SLInsert写好后就不需要这些了,直接调用
- SLInsert(psl, psl->size, x);
- }
-
- //头插
- void SLPushFront(SL* psl, SLDataType x)
- {
- //assert(psl);
- //SLCheckCapacity(psl);
-
- //挪动数据
- //int end = psl->size - 1;
- //while (end >= 0)
- //{
- // psl->a[end+1] = psl->a[end];
- // --end;
- //}
- //psl->a[0] = x;
- //psl->size++;
-
- //这里也不需要了,直接调用
- SLInsert(psl, 0, x); // -- 这就相当与头插了
-
- }
-
- //尾删
- void SLPoPBack(SL* psl)
- {
- //assert(psl);
-
- //比较温柔的检查
- //if (psl->size == 0)
- //{
- // return;
- //}
-
- //暴力的检查 -- 会直接报错 在C++中更常用
- //assert(psl->size > 0);
-
- //psl->size--; //realloc是不可以释放一部分的空间的所以不要free
- // //也不需要清除掉数据,但是没必要,可以直接覆盖
- // //这种的情况是由size为基准,由它访问
- // //但是有bug:删掉的多了就会出现数据的越界
-
- //写完之后就可以替换掉之前的了
-
- SLErase(psl, psl->size-1);
-
- }
-
- //头删
- void SLPoPhFront(SL* psl)
- {
- //assert(psl);
- //assert(psl->size > 0);
-
- //一
- //int begin = 0;
- //while (begin < psl->size - 1)
- //{
- // psl->a[begin] = psl->a[begin + 1];
- // ++begin;
- //}
- //--psl->size;
-
- //写完之后就可以替换掉之前的了
- SLErase(psl, 0);
-
- }
- //缩容:以时间换空间 -- 不常用
- //不缩容:以空间换时间 -- 现在的设备比较空间都比较强
- //realloc 缩容可能会异地缩容
-
-
- //顺序表查找 -- 没找到返回-1
- //这里直接遍历数组就可以了,在数据少的情况下这样反而更快 O(N) 级别
- int SLFind(SL* psl, SLDataType x)
- {
- assert(psl);
-
- for (int i = 0; i < psl->size; ++i)
- {
- if (psl->a[i] == x)
- {
- return i;
- }
- }
- return -1;
- }
-
- //顺序表在pos位置插入x -- 在前面插入
- void SLInsert(SL* psl, size_t pos, SLDataType x) //size_t 更符合库函数的设计
- {
- assert(psl);
- assert(pos <= psl->size);
-
- SLCheckCapacity(psl);
-
- //挪动数据 法一: 用这种方法要注意算术转化,容易死循环
- //int end = psl->size - 1;
- //while (end>=(int)pos)
- //{
- // psl->a[end] = psl->a[end - 1];
- // --end;
- //}
-
- //挪动数据 法二:这种方法更好,不需要强制转化
- size_t end = psl->size;
- while (end > pos)
- {
- psl->a[end] = psl->a[end - 1];
- end--;
- }
-
- psl->a[pos] = x;
- ++psl->size;
- }
-
-
- // 顺序表删除pos位置的值
- void SLErase(SL* psl, size_t pos)
- {
- assert(psl);
- assert(pos < psl->size);
-
- size_t begin = pos;
- while (begin < psl->size - 1)
- {
- psl->a[begin] = psl->a[begin + 1];
- ++begin;
- }
-
- --psl->size;
-
- }
-
- //修改
- void SLModify(SL* psl, size_t pos, SLDataType x)
- {
- assert(psl);
- assert(pos < psl->size);
-
- psl->a[pos] = x;
-
- }
- #include "SeqList.h"
-
- //尾插、头插、销毁测试
- void TestSeqList1()
- {
- SL s;
- //初始化
- SLInit(&s);
- //尾插
- SLPushBack(&s, 1);
- SLPushBack(&s, 2);
- SLPushBack(&s, 3);
- SLPushBack(&s, 4);
- SLPushBack(&s, 5);
- SLPushBack(&s, 6);
- //打印观察
- SLPrint(&s);
-
- //头插
- SLPushFront(&s, 10);
- SLPushFront(&s, 20);
- SLPushFront(&s, 30);
- //打印观察
- SLPrint(&s);
-
- //销毁
- SLDestory(&s);
-
- }
-
-
- //尾插、尾删、删多了测试
- TestSeqList2()
- {
- //SLInit(NULL); //测试传空指针 -- 到assert会直接报错
- SL s;
- SLInit(&s);
- SLPushBack(&s, 1);
- SLPushBack(&s, 2);
- SLPushBack(&s, 3);
- SLPushBack(&s, 4);
- SLPrint(&s);
-
- //尾删
- SLPoPBack(&s);
- SLPoPBack(&s);
- SLPrint(&s);
- //再尾删
- SLPoPBack(&s);
- SLPoPBack(&s);
- SLPrint(&s);
-
- //头删
- SLPushBack(&s,10);
- SLPushBack(&s,20);
- SLPushBack(&s,30);
- SLPushBack(&s,40);
- SLPrint(&s);
-
- SLDestory(&s);
- }
-
- //插入测试
- TestSeqList3()
- {
- SL s;
- SLInit(&s);
- SLInsert(&s, 0, 30);
- SLPushBack(&s, 40);
- SLPrint(&s);
- }
-
- //尾插、删除测试
- TestSeqList4()
- {
- SL s;
- SLInit(&s);
- SLPushBack(&s, 1);
- SLPushBack(&s, 2);
- SLPushBack(&s, 3);
- SLPushBack(&s, 4);
- SLPushBack(&s, 5);
- SLPrint(&s);
-
- SLErase(&s, 0);
- SLPrint(&s);
- int x = 0;
-
- scanf("%d", &x);
- int pos = SLFind(&s, x);
- if (pos != -1)
- {
- SLErase(&s, pos);
- }
- SLPrint(&s);
-
- }
-
- void memu()
- {
- printf("****************************\n");
- printf("1、尾插数据 2、头插数据\n");
- printf("3、位置插入 7、打印数据 \n");
- printf(" -1、退出 \n");
- printf("****************************\n");
-
- }
-
- int main()
- {
- //TestSeqList1(); //测试的时候尽量单独用一个函数测试
- //TestSeqList3();
- //TestSeqList4();
-
- SL s;
- SLInit(&s);
- int option = 0;
- int x = 0;
- int pos = 0;
- do
- {
- memu();
- printf("请输入你的操作:>");
- scanf("%d", &option);
- switch (option)
- {
- case 1:
- printf("请连续输入你想要插入的数据,以-1结束\n");
- scanf("%d", &x);
- while (x != -1)
- {
- SLPushBack(&s,x);
- scanf("%d", &x);
- }
- break;
- case 2:
- printf("请连续输入你想要插入的数据,以-1结束\n");
- scanf("%d", &x);
- while (x != -1)
- {
- SLPushFront(&s, x);
- scanf("%d", &x);
- }
- break;
- case 3:
- printf("请输入你想要插入的数据\n");
- scanf("%d", &x);
- printf("请输入你想要插入的位置\n");
- scanf("%d", &pos);
- SLInsert(&s, pos, x);
- break;
- case 7:
- SLPrint(&s);
- break;
- default:
- break;
- }
-
- } while (option != -1);
-
- SLDestory(&s);
-
- return 0;
- }
菜单不完善了,很简单
我写这篇的时候发现这里不允许使用 来二层注释
别有幽愁暗恨生,此时无声胜有声。
唐·白居易 《琵琶行》