先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:xhs1999xhs (备注Java)
正文
顺序表目录
前言
从0快速实现顺序表。
一、线性表
① 线性表(Linear list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串…
② 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物 理上存储时,通常以数组和链式结构的形式存储。
二、顺序表
1.顺序表的概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
💡顺序表顾名思义,数据是按着顺序连续存储的。
顺序表分为
- 静态顺序表
💬 使用定长数组存储元素:
#define N 7
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N]; //定长数组
size\_t size; //有效数据的个数
} SL;
📚静态顺序表的特点:如果满了就不让插入
🆘静态顺序表的缺点:设置的长度是多少呢?不能准确确定。N给小了不够用;N给大了就显得浪费
- 动态顺序表
💬 使用动态开辟的数组存储元素:
typedef int SLDataType;
typedef struct SeqList
{
SLDataType\* array; //指向动态开辟的数组
size\_t size; //有效数据的个数
size\_t capacity; //容量空间的大小
} SL;
动态顺序表可根据需要动态地分配空间,因此在现实中广泛使用。
2.接口函数
接口函数: 某个模块写了(主要)给其它模块用的函数。
📌注意:
① 接口函数的命名风格跟着STL走,便于后期学习STL
② 模块化思想:SeqList.h 头文件存放 结构体、函数的声明; SeqList.c 源文件存放 函数的定义;Test.c 源文件 用于测试。
💬主要的接口函数有:
接口函数采用址传递方式
📁SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
SLDataType\* array; //指向动态开辟的数组
size\_t size; //有效数据的个数
size\_t capacity; //容量空间的大小
} SL;
/\*接口函数\*/
//顺序表 初始化
void SeqListInit(SL\* psl);
//顺序表 尾插
void SeqListPushBack(SL\* psl, SLDateType x);
//顺序表 打印
void SeqListPrint(SL\* psl);
//顺序表 销毁
void SeqListDestory(SL\* psl);
//顺序表 尾删
void SeqListPopBack(SL\* psl);
//顺序表 头插
void SeqListPushFront(SL\* psl,SLDateType x);
//顺序表 判断扩容
void SeqListCheckCapacity(SL\* psl);
//顺序表 头删
void SeqListPopFront(SL\* psl);
//顺序表 查找
int SeqListFind(SL\* psl,SLDateType x);
//顺序表 任意pos位置插入
int SeqListInsert(SL\* psl, int pos, SLDateType x);
//顺序表 任意pos位置删除
void SeqListEarse(SL\* psl, int pos);
🔑解读:#pragma once
为了避免同一个头文件被多次包含。
顺序表 初始化
📁SeqList.c
//顺序表 初始化
void SeqListInit(SL\* psl)
{
psl->array = NULL;
psl->size = psl->capacity = 0;
}
🔑解读: 略
顺序表 尾插
💡尾插要考虑三种情况:
- 整个顺序表没有空间
- 空间已满,则扩容
- 空间是足够的,直接插入数据即可
🔱总结: 尾插需要考虑是否需要扩容
📁SeqList.c
//顺序表 尾插
void SeqListPushBack(SL\* psl, SLDataType x)
{
//判断是否需要扩容
if (psl->size == psl->capacity)
{
int newcapacity = psl->capacity == 0 ? 4 : psl->capacity\*2;
//realloc:若原空间为空,就等于malloc。调整为newcapacity个SLDataType大小的空间
SLDataType\* tmp = (SLDataType\*)realloc(psl->array, new_capacity \* sizeof(SLDataType));
//防止realloc翻车,也可以用assert(tmp);断言
if (tmp == NULL)
{
printf("realloc failed!\n");
exit(-1);
}
//更新
psl->array = tmp;
psl->capacity = newcapacity;
}
//插入
psl->array[psl->size] = x;
psl->size++;
}
🔑解读:
① 扩容时,扩大为原来capacity的2倍(2倍是比较合理、折中的倍数,你也可以选择其他的倍数)
② 为了避免realloc失败返回NULL导致后续会访问空,我们创建了临时指针tmp接收,并对其判断。
顺序表 打印
📁SeqList.c
//顺序表 打印
void SeqListPrint(SL\* psl)
{
int i = 0;
for (i = 0; i < psl->size; i++)
{
printf("%d ", psl->array[i]);
}
printf("\n");
}
🔑解读: for循环遍历顺序表,并逐一打印。
📌温馨提示:编写代码要养成写一段测试一段的习惯。切勿等写完再来测试 |
测试代码:
📁Test.c
#include "SeqList.h"
void TestSeqList1()
{
SL sl;
SeqListInit(&sl);
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPushBack(&sl, 5);
SeqListPrint(&SL);
}
int main()
{
TestSeqList1();
return 0;
}
🔑解读: TestSeqList1();
模块化测试思想
具体测试方法:【实用调试技巧】,在此不再叙述。一定要多尝试自己测试调试代码。(下文就不再带大家测试了)
顺序表 销毁
📌注意: 数组是动态开辟的,对于动态开辟的内存空间,我们要养成手动free掉的习惯,否则会有内存泄露的风险。
📁SeqList.c
//顺序表 销毁
void SeqListDestory(SL\* psl)
{
free(psl->array);
psl->array = NULL;
psl->capacity = psl->size = 0;
}
🔑解读: free掉psl->array
指向的内存空间后,要psl->array
置空,以防止其变成野指针。
顺序表 尾删
① 尾删就是让psl->size--;
(让有效数据减1即可,因为psl->size作为数组下标时是下一个插入数据的位置)
② 尾删要考虑顺序表是否为空的情况,当顺序表都空了还删除会有越界非法访问的安全问题。即尾删要考虑判空。
📁SeqList.c
//顺序表 尾删
void SeqListPopBack(SL\* psl)
{
//1.温柔手法
/\* if (psl->size > 0)
{
psl->size--;
}\*/
//2.暴力手法
assert(psl->size > 0);
psl->size--;
}
🔑解读: 尾插要考虑判空,判空建议用断言方式。
顺序表 头插 和 顺序表 扩容
📁SeqList.c
//顺序表 扩容
void SeqListCheckCapacity(SL\* psl)
{
//判断是否需要扩容
if (psl->size == psl->capacity)
{
int newcapacity = psl->capacity == 0 ? 4 : psl->capacity\*2;
//realloc:若原空间为空,就等于malloc。调整为newcapacity个SLDataType大小的空间
SLDataType\* tmp = (SLDataType\*)realloc(psl->array, new_capacity \* sizeof(SLDataType));
//防止realloc翻车,也可以用assert(tmp);断言
if (tmp == NULL)
{
printf("realloc failed!\n");
exit(-1);
}
//更新
psl->array = tmp;
psl->capacity = newcapacity;
}
}
//顺序表 头插
void SeqListPushFront(SL\* psl, SLDateType x)
{
SeqListCheckCapacity(psl);//复用
int end = psl->size - 1;//psl->size作为数组下标时是下一个插入数据的位置,所以要减1
while (end >= 0)
{
psl->array[end + 1] = psl->array[end];
end--;
}
//此时,array[0]被挪到后一位
psl->array[0] = x;
psl->size++;
}
🔑解读:
① 插入要考虑是否需要扩容,头插和尾插都需要扩容,我们可以将扩容操作封装为一个SeqListCheckCapacity函数,再在头插和尾插中复用。
② 顺序表的数据是从头开始连续存储的。所以顺序表实现头插,需要将所有元素往后挪动1位。int end = psl->size - 1;
按psl->array[end]、psl->array[end - 1]…的顺序依次往后挪1位
⚡优化顺序表 尾删
在尾插中复用扩容
📁SeqList.c
//顺序表 尾插
void SeqListPushBack(SL\* psl, SLDateType x)
{
SeqListCheckCapacity(psl);//复用扩容函数
psl->array[psl->size] = x;
psl->size++;
}
优化后可读性提高!
顺序表 头删
📁SeqList.c
//顺序表 头删
void SeqListPopFront(SL\* psl)
{
assert(psl->size > 0);
int begin = 1;
while (begin < psl->size)
{
psl->array[begin - 1] = psl->array[begin];
begin++;
}
psl->size--;
}
🔑解读:
① 删除需要考虑判空
② int begin = 1;
按psl->array[1]、psl->array[2]…的顺序依次往前挪1位,最后psl->size--;
顺序表 查找
📁SeqList.c
//顺序表 查找
int SeqListFind(SL\* psl,SLDateType x)
{
//数组下标从0开始
int i = 0;
for (i = 0; i < psl->size; i++)
{
if (psl->array[i] == x)
{
return i;
}
}
//没找到
return -1;
}
🔑解读:
比较简单,for循环依次遍历顺序表 若 psl->array[i] == x
,则返回下标 i
;没找到就返回 -1
。
时间复杂度为O(N)
。可以尝试写一个二分查找算法的接口函数实现,但前提要排序。
顺序表 任意pos位置插入
前面学习了顺序表的头插和尾插,我们能不能在任意的pos位置插入呢?这得考虑考虑!
因此我们要对pos进行条件限定。另外,插入就得考虑是否需要扩容。
📁SeqList.c
//顺序表 任意pos位置插入
int SeqListInsert(SL\* psl, int pos, SLDateType x)
{
//插入的pos位置有区域约束
//1.温柔方法
/\* if (pos < 0 && pos > psl->size)
{
printf("pos invalidd(pos非法!)\n");
return;
}\*/
//2.暴力方法
assert(pos >= 0 && pos <= psl->size);
//凡是插入,考虑是否需要扩容
SeqListCheckCapacity(psl);
//挪数据
int end = psl->size - 1;
while (end >= pos)
{
psl->array[end + 1] = psl->array[end];
end--;
}
//正式插入
psl->array[pos] = x;
psl->size++;
}
🔑解读:
思路类似头插,只是while循环的终止条件发生变化。
🔱对头插函数和尾插函数的优化
SeqListInsert函数可以在任意pos位置插入数据,当然包括了头部和尾部。因此我们可以在头插和尾插函数中复用SeqListInsert函数。真是妙极了!
头插函数复用SeqListInsert函数
//顺序表 头插(复用SeqListInsert函数)
void SeqListPushFront(SL\* psl, SLDataType x)
{
SeqListInsert(psl, 0, x);
}
尾插函数复用SeqListInsert函数
//顺序表 尾插(复用SeqListInsert函数
void SeqListPushBack(SL\* psl, SLDataType x)
{
SeqListInsert(psl, psl->size, x);
}
顺序表 任意pos位置删除
① 类似任意pos位置插入,我们也可以在pos位置删除。同样pos位置是有条件限定的,特别的是注意psl->size无有效数据,不可删除。
② 删除就得考虑判空
//顺序表 任意pos位置删除
void SeqListEarse(SL\* psl, int pos)
{
//删除和插入一样:有区域约束,不可任意访问删除
//1.温柔方法
/\* if (pos < 0 && pos >= 0)
{
printf("pos invalidd!\n");
return;
}\*/
//2.暴力处理
assert(pos >= 0 && pos < psl->size);
int begin = pos;
while (begin < psl->size)
{
psl->array[begin] = psl->array[begin + 1];
begin++;
}
psl->size--;
}
🔑解读:
思路类似头删。
🔱对头删函数和尾删函数的优化
同样的道理,头删和尾删也可以复用SeqListEarse函数
头删函数复用SeqListEarse函数
//顺序表 头删(复用SeqListEarse函数)
void SeqListPopFront(SL\* psl)
{
SeqListEarse(psl, 0);
}
尾删函数复用SeqListEarse函数
//顺序表 尾删(复用SeqListEarse函数)
void SeqListPopBack(SL\* psl)
{
SeqListEarse(psl, psl->size - 1);
}
到此,想必速通万家们已经知道如何快速实现顺序表了!
我们把SeqListInsert函数和SeqListEarse函数写了,就相当于把头插尾插、头删尾删写了(复用即可)
四、完整代码
📁SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
创建静态顺序表
//#define N 10
//typedef int SLDateType;
//
//typedef struct SeqList
//{
// SLDateType array[N];//定长数组
// SLDateType size;//有效个数
//}SL;
//创建动态顺序表
typedef int SLDateType;
typedef struct SeqList
{
SLDateType\* array;//指向动态开辟的数组
SLDateType size;//有效个数
SLDateType capacity;//容量空间大小
}SL;
/\*接口函数\*/
//顺序表 初始化
void SeqListInit(SL\* psl);
//顺序表 尾插
void SeqListPushBack(SL\* psl, SLDateType x);
//顺序表 打印
void SeqListPrint(SL\* psl);
//顺序表 销毁
void SeqListDestory(SL\* psl);
//顺序表 尾删
void SeqListPopBack(SL\* psl);
//顺序表 头插
void SeqListPushFront(SL\* psl,SLDateType x);
//顺序表 判断扩容
void SeqListCheckCapacity(SL\* psl);
//顺序表 头删
void SeqListPopFront(SL\* psl);
//顺序表 查找
int SeqListFind(SL\* psl,SLDateType x);
//顺序表 任意pos位置插入
int SeqListInsert(SL\* psl, int pos, SLDateType x);
//顺序表 任意pos位置删除
void SeqListEarse(SL\* psl, int pos);
📁SeqList.c
#include"SeqList.h"
//顺序表 初始化
void SeqListInit(SL\* psl)
{
psl->array = NULL;
psl->size = psl->capacity = 0;
}
//顺序表 尾插
void SeqListPushBack(SL\* psl, SLDateType x)
{
//插入
SeqListCheckCapacity(psl);
psl->array[psl->size] = x;
psl->size++;
}
//顺序表 打印
void SeqListPrint(SL\* psl)
{
int i = 0;
for (i = 0; i < psl->size; i++)
{
printf("%d ", psl->array[i]);
}
printf("\n");
}
//顺序表 销毁
void SeqListDestory(SL\* psl)
{
free(psl->array);
psl->array = NULL;
psl->capacity = psl->size = 0;
}
//顺序表 尾删
void SeqListPopBack(SL\* psl)
{
1.温柔手法
//if (psl->size > 0)
//{
// psl->size--;
//}
//没有考虑到"删过头"的情况,会非法访问
psl->array[psl->size - 1] = 0;
//psl->size--;
//2.暴力手法
//assert(psl->size > 0);
//psl->size--;
//复用SeqListEarse
SeqListEarse(psl, psl->size - 1);
}
//顺序表 头插
void SeqListPushFront(SL\* psl, SLDateType x)
{
SeqListCheckCapacity(psl);
int end = psl->size - 1;
while (end >= 0)
{
psl->array[end + 1] = psl->array[end];
end--;
}
//此时,array[0]被挪到后一位
psl->array[0] = x;
psl->size++;
}
//顺序表 判断扩容
void SeqListCheckCapacity(SL\* psl)
{
if (psl->size == psl->capacity)//判断空间是否足够
{
//扩容
SLDateType newcapacity = psl->capacity == 0 ? 4 : psl->capacity \* 2;
SLDateType\* tmp = (SLDateType\*)realloc(psl->array, newcapacity \* sizeof(SLDateType));;
if (tmp == NULL)
{
printf("扩容失败!\n");
exit(-1);
}
//扩容后需要更新的
psl->array = tmp;
psl->capacity = newcapacity;
}
}
//顺序表 头删
void SeqListPopFront(SL\* psl)
{
//assert(psl->size > 0);
//int begin = 1;
//while (begin<psl->size)
//{
// psl->array[begin - 1] = psl->array[begin];
// begin++;
//}
//psl->size--;
//复用SeqListEarse
SeqListEarse(psl, 0);
}
//顺序表 查找
int SeqListFind(SL\* psl,SLDateType x)
{
//数组下标从0开始
int i = 0;
for (i = 0; i < psl->size; i++)
{
if (psl->array[i] == x)
{
return i;
}
}
//没找到
return -1;
}
//顺序表 任意pos位置插入
int SeqListInsert(SL\* psl, int pos, SLDateType x)
{
//插入有区域约束
//温柔处理:
/\* if (pos < 0 && pos > psl->size)
{
printf("pos invalidd(pos非法!)\n");
return;
}\*/
//暴力解决
assert(pos >= 0 && pos <= psl->size);
//凡是插入,考虑是否需要扩容
SeqListCheckCapacity(psl);
//挪数据
int end = psl->size - 1;
while (end>=pos)
{
psl->array[end + 1] = psl->array[end];
end--;
}
//正式插入
psl->array[pos] = x;
psl->size++;
}
//顺序表 任意pos位置删除
void SeqListEarse(SL\* psl, int pos)
{
//删除和插入一样:有区域约束,不可任意访问删除
//温柔处理
/\* if (pos < 0 && pos >= 0)
{
printf("pos invalidd(pos非法!)\n");
return;
}\*/
//暴力处理
assert(pos >= 0 && pos < psl->size);
int begin = pos;
while (begin < psl->size)
{
psl->array[begin] = psl->array[begin + 1];
begin++;
}
psl->size--;
}
📁Test.c
#include"SeqList.h"
void TestSeqList1()
{
SL sl;
SeqListInit(&sl);
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPushBack(&sl, 5);
SeqListPrint(&sl);
//SeqListPopBack(&sl);
//SeqListPopBack(&sl);
//SeqListPopBack(&sl);
//SeqListPopBack(&sl);
//SeqListPopBack(&sl);
//SeqListPopBack(&sl);
//SeqListPrint(&sl);
//SeqListPushBack(&sl, 10);
//SeqListPushBack(&sl, 20);
//SeqListPrint(&sl);
//SeqListPushFront(&sl, 1);
//SeqListPushFront(&sl, 2);
//SeqListPushFront(&sl, 3);
//SeqListPushFront(&sl, 4);
//SeqListPrint(&sl);
//SeqListDestory(&sl);
}
void TestSeqList2()
{
SL sl;
SeqListInit(&sl);
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPrint(&sl);
SeqListPushFront(&sl, 1);
SeqListPushFront(&sl, 2);
SeqListPushFront(&sl, 3);
SeqListPushFront(&sl, 4);
SeqListPrint(&sl);
//SeqListDestory(&sl);
}
void TestSeqList3()
{
SL sl;
SeqListInit(&sl);
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPrint(&sl);
//int ret = SeqListFind(&sl, 3);
//if (ret == -1)
// printf("找不到!\n");
//else
// printf("找到了,下标为%d", ret);
SeqListInsert(&sl, 5, 5);
SeqListPrint(&sl);
SeqListDestory(&sl);
}
void TestSeqList4()
{
SL sl;
SeqListInit(&sl);
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPushFront(&sl, 1);
SeqListPushFront(&sl, 2);
SeqListPushFront(&sl, 3);
SeqListPushFront(&sl, 4);
SeqListPrint(&sl);
SeqListPopFront(&sl);
SeqListPopBack(&sl);
SeqListPrint(&sl);
SeqListDestory(&sl);
}
int main()
{
//TestSeqList1();
//TestSeqList2();
//TestSeqList3();
TestSeqList4();
}
简单的三连是对作者的最大支持!
✒️ 笔者:陈汉新
📅 更新: 2022.4.13
❌ 勘误: 暂无
📣 声明:由于作者水平有限,本文错误之处在所难免,敬请读者指正!
最后
现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:xhs1999xhs (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
);
SeqListPrint(&sl);
SeqListDestory(&sl);
}
int main()
{
//TestSeqList1();
//TestSeqList2();
//TestSeqList3();
TestSeqList4();
}
---
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c59820d92836cdbb575cdac2e27c144f.png)
**简单的三连是对作者的最大支持!**
![请添加图片描述](https://i-blog.csdnimg.cn/blog_migrate/7dd4bcdd50ec68d854c4a1659f8e1311.gif)
>
> ✒️ 笔者:陈汉新
>
>
> 📅 更新: 2022.4.13
>
>
> ❌ 勘误: 暂无
>
>
> 📣 声明:由于作者水平有限,本文错误之处在所难免,敬请读者指正!
>
>
>
### 最后
现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**
[外链图片转存中...(img-Za7MCHvd-1713688617129)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**需要这份系统化的资料的朋友,可以添加V获取:xhs1999xhs (备注Java)**
[外链图片转存中...(img-8AEs4Uj2-1713688617129)]
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**