赞
踩
必备基础知识:结构体、指针(一级指针、二级指针、指针传参)、结构体指针、动态内存管理
概念:数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在⼀种或多种特定关系
的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么⽅式构成,以及数据元素之间呈现的结构。
总结:
1)能够存储数据(如顺序表、链表等结构)
2)存储的数据能够⽅便查找
eg:有两个牧场,A牧场是放养的羊,B牧场是每只羊都有各自的号码的羊,如果我们想找到叫“肖恩”的羊,在A牧场就无法快速的找到,而B牧场只需要找到“肖恩”的号码即可快速找到它。
线性表( linear list )是n个具有相同特性的数据元素的有限序列。线性表是⼀种在实际中广泛使用的据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
案例:蔬菜分为绿叶类、⽠类、菌菇类。线性表指的是具有部分相同特性的⼀类数据结构的集合。
· 顺序表和数组的区别
· 顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接口
举个例子
· 顺序表分类
· 静态顺序表
- #define N 100
- typedef int SLDataType//等效替换int,可以实现一键替换想要的数据类型
-
- struct SeqList
- {
- SLDataType a[N];//数组的长度即空间的大小
- int size;//有效数据个数
- };
静态顺序表的缺陷:给定的数组长度,若不够就会导致后续数据的保存失败(导致数据丢失,引发严重技术事故),但如果给多了就会导致空间的大量浪费。
· 动态顺序表(可增容)
- typedef int SLDataType;
-
- typedef struct SeqList
- {
- SLDataType* arr;//存储数据的底层结构
- int capacity;//记录顺序表的空间大小
- int size;//记录顺序表当前有效的数据个数
- }SL;
-
- //typedef struct SeqList SL; //与以上代码相同的去定义SeqList结构体变量SL
SeqList.h 定义顺序表的结构,顺序表要实现的接口/方法
SeqList.c 具体实现顺序表里定义的接口/方法
test.c 测试动作:测试顺序表
错误示范
SeqList.h
void SLInit(SL s1);
SeqList.c
- #include "SeqList.h"
-
- void SLInit(SL* ps)
- {
- s.arr = NULL;
- s.size = s.capacity = 0;
- }
test.c
- #include "SeqList.h"
-
- void slTest01()
- {
- SL s1;
- SLInit(s1);
- }
-
- int main()
- {
- s1Test01();
- return 0;
- }
错误:这里传的不是地址而是传的sl的值过去给s,形参只是实参的值的零食拷贝,而并不是sl本身,这样程序在运行时就会报错“使用了未初始化的局部变量sl”。
正确示范
SeqList.h
- //初始化
- void SLInit(SL* ps);
SeqList.c
- #include "SeqList.h"
-
- void SLInit(SL* ps)
- {
- ps->arr = NULL;
- ps->size = ps->capacity = 0;
- }
test.c
- #include "SeqList.h"
-
- void slTest01()
- {
- SL s1;
- SLInit(&s1);
- }
-
- int main()
- {
- s1Test01();
- return 0;
- }
SeqList.h
void SLCheckCapacity(SL* ps);
SeqList.c
- void SLCheckCapacity(SL* ps)
- {
- if(ps=>size == ps->capacity)
- {
- int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
- //判断capacity是否为零,因为初始化时capacity为零,如果不为零则扩容两倍
- SLDataType* tmp = (SLDataType*)realloc(ps=>arr, newCapacity * sizeof(SLDataType));
- //注意newCapacity的单位,要乘上数据类型的大小
- //设一个零时变量tmp是为了防止realloc开辟空间失败导致地址丢失
- if(tmp == NULL)
- {
- perror("realloc fail!");
- exit(1);
- }
- //扩容成功
- ps->arr = tmp;
- ps->capacity = newCapacity;
- }
- }
SeqList.h
void SLPrint(SL* ps); //这里可以传值也可以传地址,这里传地址是为了保持接口一致性
SeqList.c
- void SLPrint(SL* ps)
- {
- for(int i = 0; i < ps->size; i++)
- {
- printf("%d", ps->arr[i]);
- }
- printf("\n");
- }
插入数据时很可能出现两种情况
SeqList.h
void SLPushBack(SL* ps, SLDataType x);
SeqList.c
- void SLPushBack(SL* ps, SLDataType x)
- {
- //为了防止传的地址为NULL
- //assert--粗暴的解决方式,如果等于NULL或者判断为假就会报错,程序无法继续运行下去
- assert(ps);
- //assert(ps != NULL);//等同于上一行
-
- //if判断--温柔的解决方式,如果等于NULL就就直接跳过而不插入
- //if(ps == NULL)
- //{
- // return;
- //}
-
- //空间不够,扩容
- SLCheckCapacity(ps);
-
- //空间足够,直接插入
- ps->arr[ps->size++] = x;
- }
test.c
- //测试尾插
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPrint(&sl);
- SLPushBack(&sl, 5);
- SLPrint(&sl);
输出结果为
- 1 2 3 4
- 1 2 3 4 5
SeqList.h
void SLPushFront(SL* ps, SLDataType x);
SeqList.c
- void SLPushFront(SL* ps, SLDataType x)
- {
- assert(ps);
-
- //判断是否扩容
- SLCheckCapacity(ps);
-
- //旧的数据往后挪一位
- for(int i = ps->size; i > 0; i--) //i最后值为1
- {
- ps->arr[i] = ps->arr[i-1]; //最后一个循环为 ps->arr[1] = ps->arr[0]
- }
- ps->arr[0] = x;
- ps->size++;
- }
test.c
- //测试头插
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPrint(&sl);
-
- SLPuchFront(&sl, 5);
- SLPuchFront(&sl, 6);
- SLPuchFront(&sl, 7);
- SLPrint(&sl);
输出结果为
- 1 2 3 4
- 7 6 5 1 2 3 4
SeqList.h
void SLPopBack(SL* ps);
SeqList.c
- void SLPopBack(SL* ps)
- {
- assert(ps);
- assert(ps->size); //判断顺序表是否为空,为空不能进行删除操作
-
- //顺序表不为空
- ps->size--; //这里属于是看不见就等于没有,这并不影响数据的查找,修改,插入
- }
test.c
- //测试尾删
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPrint(&sl);
-
- SLPopBack(&sl);
- SLPopBack(&sl);
- SLPrint(&sl);
输出结过为
- 1 2 3 4
- 1 2
SeqList.h
void SLPopFront(SL* ps);
SeqList.c
- void SLPopFront(SL* ps)
- {
- assert(ps);
- assert(ps->size);
-
- //不为空执行挪动操作
- for(int i = 0; i < size-1; i++) //i最后的值为size-2
- {
- ps->arr[i] = ps->arr[i+1]; //最后一次循环为 ps->arr[size-2] = ps->arr[size-1]
- }
- ps->size--;
- }
test.c
- //测试头删
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPrint(&sl);
-
- SLPopFront(&sl);
- SLPopFront(&sl);
- SLPrint(&sl);
输出结果为
- 1 2 3 4
- 3 4
SeqList.h
void SLInsert(SL* ps, int pos, SLDataType x);
SeqList.c
- void SLInsert(SL* ps, int pos, SLDataType x)
- {
- assert(ps);
- assert(pos >= 0 && pos <= ps->size); //限制pos的大小,否则会出现插入失败的情况
-
- SLCheckCapacity(ps);
-
- //pos及之后的数据往后挪动一位,把pos空出来
- for(int i = ps->size; i > pos; i--)
- {
- ps->arr[i] = ps->arr[i-1]; //最后一步ps->arr[pos+1] = ps->arr[pos]
- }
- ps->arr[pos] = x;
- ps->size++;
- }
test.c
- //测试指定位置插入
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPrint(&sl);
-
- SLInsert(&sl, 0, 100);
- SLPrint(&sl);
- SLInsert(&sl, 0, 100);
- SLPrint(&sl);
-
- //SLInsert(&sl, 0, 100); //如果代码中没有assert来检查pos的值,这里就会插入失败,插入的数据异常
- //SLPrint(&sl);
输出结果为
- 1 2 3 4
- 100 1 2 3 4
- 100 1 2 3 4 200
SeqList.h
void SLErase(SL* ps, int pos);
SeqList.c
- void SLErase(SL* ps, int pos)
- {
- assert(ps);
- assert(pos >= 0 && ps < ps->size);
-
- //pos以后的数据往前挪一位
- for(int i = pos; i < ps->size-1; i++)
- {
- ps->arr[i] = ps->arr[i+1]; //最后一步ps->arr[i-2] = ps->arr[i-1]
- }
- ps->size--;
- }
test.c
- //测试指定位置删除
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPrint(&sl);
-
- SLErase(&sl, 0);
- SLPrint(&sl);
- SLErase(&sl, sl.size - 1);
- SLPrint(&sl);
输出结果为
- 1 2 3 4
- 2 3 4
- 2 3
SeqList.h
int SLFind(SL* ps, SLDataType x);
SeqList.c
- int SLFind(SL* ps, SLDataType x)
- {
- assert(ps); //加上断言对代码的健壮性更好
- for(int i = 0; i < ps->size; i++)
- {
- if(ps->arr[i] == x)
- {
- return i;
- }
- }
- return -1;
- }
test.c
- //测试查找
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPrint(&sl);
-
-
- int ret = SLFind(&Sl, 3);
- if(ret < 0)
- {
- printf("数据不存在,查找失败!!");
- }
- else
- {
- printf("数据找到了,在下标为%d的位置\n", ret);
- }
-
- //int ret = SLFind(&Sl, 6);
- //if(ret < 0)
- //{
- // printf("数据不存在,查找失败!!“);
- //}
- //else
- //{
- // printf("数据找到了,在下标为%d的位置!\n", ret);
- //}
输出结果为
- 1 2 3 4
- 数据找到了,在下标为2的位置
-
- //数据不存在,查找失败!!
SeqList.h
void SLDestroy(SL* ps);
SeqLis.c
- void SLDestroy(SL* ps)
- {
- assert(ps);
- if(ps->arr) //如果arr本身就是空的,我们就没必要销毁了
- {
- free(ps->arr);
- }
- ps->arr = NULL;
- ps->size = ps->capacity = 0;
- }
SeqList.h
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
-
- typedef int SLDataType//等效替换int,可以实现一键替换想要的数据类型
- typedef struct SeqList
- {
- SLDataType* arr;//存储数据的底层结构
- int capacity;//记录顺序表的空间大小
- int size;//记录顺序表当前有效的数据个数
- }SL;
-
- //typedef struct SeqList SL; //与以上代码相同的去定义SeqList结构体变量SL
-
- //初始化
- void SLInit(SL s1);
- //扩容
- void SLCheckCapacity(SL* ps);
- //打印
- void SLPrint(SL* ps);
- //尾插
- void SLPushBack(SL* ps, SLDataType x);
- //头插
- void SLPushFront(SL* ps, SLDataType x);
- //尾删
- void SLPopBack(SL* ps);
- //头删
- void SLPopFront(SL* ps);
- //指定位置插入数据
- void SLInsert(SL* ps, int pos, SLDataType x);
- //删除指定位置数据
- void SLErase(SL* ps, int pos);
- //查找数据
- int SLFind(SL* ps, SLDataType x);
- //销毁
- void SLDestroy(SL* ps);
SeqList.c
- #include "SeqList.h"
-
- //初始化
- void SLInit(SL* ps)
- {
- ps->arr = NULL;
- ps->size = ps->capacity = 0;
- }
-
- //扩容
- void SLCheckCapacity(SL* ps)
- {
- if(ps->size == ps->capacity)
- {
- int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
- //判断capacity是否为零,因为初始化时capacity为零,如果不为零则扩容两倍
- SLDataType* tmp = (SLDataType*)realloc(ps=>arr, newCapacity * sizeof(SLDataType));
- //注意newCapacity的单位,要乘上数据类型的大小
- //设一个零时变量tmp是为了防止realloc开辟空间失败导致地址丢失
- if(tmp == NULL)
- {
- perror("realloc fail!");
- exit(1);
- }
- //扩容成功
- ps->arr = tmp;
- ps->capacity = newCapacity;
- }
- }
-
- //打印
- void SLPrint(SL* ps)
- {
- for(int i = 0; i < ps->size; i++)
- {
- printf("%d", ps->arr[i]);
- }
- printf("\n");
- }
-
- //尾插
- void SLPushBack(SL* ps, SLDataType x)
- {
- //为了防止传的地址为NULL
- //assert--粗暴的解决方式,如果等于NULL或者判断为假就会报错,程序无法继续运行下去
- assert(ps);
- //assert(ps != NULL);//等同于上一行
-
- //if判断--温柔的解决方式,如果等于NULL就就直接跳过而不插入
- //if(ps == NULL)
- //{
- // return;
- //}
-
- //空间不够,扩容
- 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--) //i最后值为1
- {
- ps->arr[i] = ps->arr[i-1]; //最后一个循环为 ps->arr[1] = ps->arr[0]
- }
- ps->arr[0] = x;
- ps->size++;
- }
-
- //尾删
- void SLPopBack(SL* ps)
- {
- assert(ps);
- assert(ps->size); //判断顺序表是否为空,为空不能进行删除操作
-
- //顺序表不为空
- ps->size--; //这里属于是看不见就等于没有,这并不影响数据的查找,修改,插入
- }
-
- //头删
- void SLPopFront(SL* ps)
- {
- assert(ps);
- assert(ps->size);
-
- //不为空执行挪动操作
- for(int i = 0; i < size-1; i++) //i最后的值为size-2
- {
- ps->arr[i] = ps->arr[i+1]; //最后一次循环为 ps->arr[size-2] = ps->arr[size-1]
- }
- ps->size--;
- }
-
- //指定位置插入数据
- void SLInsert(SL* ps, int pos, SLDataType x)
- {
- assert(ps);
- assert(pos >= 0 && pos <= ps->size); //限制pos的大小,否则会出现插入失败的情况
-
- SLCheckCapacity(ps);
-
- //pos及之后的数据往后挪动一位,把pos空出来
- for(int i = ps->size; i > pos; i--)
- {
- ps->arr[i] = ps->arr[i-1]; //最后一步ps->arr[pos+1] = ps->arr[pos]
- }
- ps->arr[pos] = x;
- ps->size++;
- }
-
- //删除指定位置数据
- void SLErase(SL* ps, int pos)
- {
- assert(ps);
- assert(pos >= 0 && ps < ps->size);
-
- //pos以后的数据往前挪一位
- for(int i = pos; i < ps->size-1; i++)
- {
- ps->arr[i] = ps->arr[i+1]; //最后一步ps->arr[i-2] = ps->arr[i-1]
- }
- ps->size--;
- }
-
- //查找数据
- int SLFind(SL* ps, SLDataType x)
- {
- assert(ps); //加上断言对代码的健壮性更好
- for(int i = 0; i < ps->size; i++)
- {
- if(ps->arr[i] == x)
- {
- return i;
- }
- }
- return -1;
- }
-
- //销毁
- void SLDestroy(SL* ps)
- {
- assert(ps);
- if(ps->arr) //如果arr本身就是空的,我们就没必要销毁了
- {
- free(ps->arr);
- }
- ps->arr = NULL;
- ps->size = ps->capacity = 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。