既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
typedef int SLDataType;//顺序表中存储的数据,此处假设是int型
typedef struct SeqList
{
int* a;//指向动态开辟的数组空间,空间可以随时增容
int size;//存储数据个数
int capacity;//存储空间大小
}SL,SeqList;
2.2.2 顺序表初始化
void SeqListInit(SeqList* psl);//声明
void SeqListInit(SeqList* psl)
{
assert(psl);//进行断言是因为当psl为NULL时,下面的操作将无法进行,因为空指针是无法进行解引用的。
psl->a = NULL;
psl->size = 0;
psl->capacity = 0;
}//函数实现
注意:进行断言是因为当psl为NULL时,下面的操作将无法进行,因为空指针是无法进行解引用的,后面也是如此。
2.2.3 顺序表的销毁
void SeqListDestroy(SeqList* psl);
void SeqListDestroy(SeqList* psl)
{
assert(psl);
free(psl->a);
psl->a = NULL;
psl->capacity = psl->size = 0;
}
注意:free后面括号中的指针必须是malloc开辟出来的那块空间,且不能有任何的偏差(即指针不能发生移动)。
下面进行举例:
像上面这样使用是完全没有问题的,但是像下面这样进行使用就出现了问题:
tmp进行自增操作后,就指向了下图所示位置:
free的位置是tmp++后的位置,这不符合C语言的规定,且即使正常的释放掉了,前面的那一块int空间也将引起内存泄漏问题,即动态开辟的内存忘记释放。
2.2.4 顺序表的尾插
void SeqListPushBack(SeqList* psl,SLDataType x);//声明
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);
//如果满了,就进行扩容
SeqListCheckCapacity(psl);
psl->a[psl->size] = x;
psl->size++;
}
2.2.5 顺序表的尾删
void SeqListPopBack(SeqList* psl);
void SeqListPopBack(SeqList* psl)
{
assert(psl);
if(psl->size > 0)
{
psl->size -= 1;
}
}
2.2.6 顺序表的头插
void SeqListPushFront(SeqList* psl, SLDataType x);
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListCheckCapacity(psl);
int end = psl->size - 1;
while (end >= 0)
{
psl->a[end+1] = psl->a[end];
--end;
}
psl->a[0] = x;
psl->size++;
}
顺序表的头插会涉及到后续元素的移动,头插时要将顺序表中的元素从后面开始进行移动,因为从前面开始移动的话会出现覆盖现象。下面是图示:
2.2.7 顺序表的头删
同理,如果想要元素不被覆盖,就只能从前向后进行移动。
void SeqListPopFront(SeqList* psl);
void SeqListPopFront(SeqList* psl)
{
//挪出数据,腾出头部空间
//方法一:从1开始移动
/*assert(psl);
if (psl->size > 0)
{
int begin = 1;
while (begin < psl->size)
{
psl->a[begin - 1] = psl->a[begin];
begin++;
}
psl->size--;
}*/
//方法二:从0开始移动
assert(psl);
if (psl->size > 0)
{
int begin = 0;
while (begin < psl->size - 1)
{
psl->a[begin] = psl->a[begin + 1];
begin++;
}
psl->size--;
}
}
下图是两种移动方式的区别:
问:为什么不可以直接将指针进行加1操作?
- free释放空间时的指针和malloc开辟空间的指针必须相同
- mallo开辟的空间存在浪费,即0的那块空间被浪费,无法进行使用,因为那块空间是被合法申请的。
2.2.8 顺序表容量的检查与扩容
void SeqListCheckCapacity(SeqList* psl);
void SeqListCheckCapacity(SeqList* psl)
{
assert(psl);
if (psl->capacity == psl->size)
{
size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
psl->a = tmp;
psl->capacity *= 2;
}
}
}
注意点1:此处考虑使用的是如果容量不够,就将容量扩容为原容量的两倍,但是最开始的容量是0,所以要考虑到最开始为0的情况。
注意点2:要对扩容是否成功进行检测,即判断刚申请的空间是否为空。
2.2.9 顺序表任意位置的插入
void SeqListInsert(SeqList* psl,size_t pos,SLDataType x);
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
//较为温和的检查方式
/*if (pos > psl->size)
{
exit(-1);
}*/
assert(pos <= psl->size);//较为暴力的检查方式
SeqListCheckCapacity(psl);
size_t end = psl->size;
while (end > pos)
{
psl->a[end] = psl->a[end-1];
--end;
}
psl->a[pos] = x;
psl->size++;
}
注意:
问:为什么end从psl->size开始?
答:此处需要注意的是pos和end的类型,为什么呢?因为两者都是无符号类型,所以尤其注意当end到了-1的时候,就会变成一个很大的数字,此时如果以此作为下标进行访问,一定会出现越界访问内存的情况,考虑一种极端情况,当pos为0的时候,end最小的时候就是为0,所以此时不会出现越界的情况,但是如果end是从psl->size - 1开始的话,那么while循环体内的语句就变成下面这样:
![img](https://img-blog.csdnimg.cn/img_convert/a537ee406e55c795497c78b2b3cdea7b.png)
![img](https://img-blog.csdnimg.cn/img_convert/91a046f261514cb0d165d4dc6ea076f7.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
外链图片转存中...(img-bO10xXcp-1715800788435)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**