当前位置: 首页 > news >正文

微服务的编程测评系统13-我的竞赛列表-elasticSearch

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 1. 我的竞赛列表
    • 1.1 遗留问题
    • 1.2 后端开发
    • 1.3 竞赛报名前端开发
  • 2. 题目列表功能
    • 2.1 elasticSearch基本知识
    • 2.2 elasticSearch使用
    • 2.2 Kibana
  • 3. es命令
    • 3.1 PUT增加
    • 3.2 GET查询
    • 3.3 DELETE删除
    • 3.4 POST修改
    • 3.5 分词器
    • 3.6 安装ik分词器
    • 3.7 分词模式
    • 3.8 项目引入
  • 总结


前言

1. 我的竞赛列表

1.1 遗留问题

处理完用户请求之后,要清理ThreadLocal里面的数据
—》拦截器–》after可以完成这个功能–》清理所有的数据,因为这个线程已经没了,数据肯定也要清理掉了

    @Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {ThreadLocalUtil.remove();}

这样就可以了

1.2 后端开发

    @GetMapping("/list")public TableDataInfo list(ExamQueryDTO examQueryDTO){log.info("获取用户报名的竞赛信息,examQueryDTO:{}", examQueryDTO);return userExamService.list(examQueryDTO);}

直接复制获取exam历史和已发布的竞赛的代码,然后改改

@AllArgsConstructor
@Getter
public enum ExamListType {EXAM_UN_FINISH_LIST(0),EXAM_HISTORY_LIST(1),USER_EXAM_LIST(2);private final Integer value;}
    private String getExamListKey(Integer examListType,Long userId) {if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {return CacheConstants.EXAM_UNFINISHED_LIST;} else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {return CacheConstants.EXAM_HISTORY_LIST;}else if(ExamListType.USER_EXAM_LIST.getValue().equals(examListType)){return CacheConstants.  +userId;}return null;}

尤其是改ExamCacheManager,主要就是增加了userid

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ck.friend.mapper.user.UserExamMapper"><select id="selectUserExamList" resultType="com.ck.friend.domain.exam.vo.ExamVO">SELECTe.exam_id,e.title,e.start_time,e.end_timeFROMtb_user_exam ueJOINtb_exam eONue.exam_id = e.exam_idWHEREue.user_id = #{userId}ORDER BYue.create_time DESC</select>
</mapper>
    @Overridepublic TableDataInfo list(ExamQueryDTO examQueryDTO) {examQueryDTO.setType(ExamListType.USER_EXAM_LIST.getValue());Long userId= ThreadLocalUtil.get(Constants.USER_ID,Long.class);Long listSize = examCacheManager.getListSize(examQueryDTO.getType(),userId);List<ExamVO> list ;if(listSize==null||listSize==0){//说明缓存中没有数据,所以要先从数据库中获取数据,然后存入redisPageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());list = userExamMapper.selectUserExamList(userId);examCacheManager.refreshCache(examQueryDTO.getType(),userId);long total = new PageInfo<>(list).getTotal();return TableDataInfo.success(list, total);}else{//直接从redis中获取数据list = examCacheManager.getExamVOList(examQueryDTO,userId);listSize = examCacheManager.getListSize(examQueryDTO.getType(),userId);return TableDataInfo.success(list, listSize);}}

这样就没有问题了

1.3 竞赛报名前端开发

在这里插入图片描述
竞赛开始前:没有报名显示未报名,已经报名显示已报名
竞赛开始后:没有报名显示已开赛,已经报名显示开始答题

所以我们要加上按钮

在这里插入图片描述

@Data
public class ExamVO {@JsonSerialize(using = ToStringSerializer.class)private Long examId;private String title;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime startTime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime endTime;private boolean enter;//true表示已经报名,false表示没有报名}

开发之前我们要先完善后端,后端返回的examList要自带是否报名的字段
在ExamCacheManager中增加方法

    public List<Long> getAllUserExam(Long userId) {String detailKey = getDetailKey(userId);List<Long> cacheListByRange = redisService.getCacheListByRange(detailKey, 0, -1, Long.class);if(CollectionUtil.isNotEmpty(cacheListByRange)){return cacheListByRange;}List<UserExam> userExams = userExamMapper.selectList(new LambdaQueryWrapper<UserExam>().eq(UserExam::getUserId, userId));refreshCache(ExamListType.USER_EXAM_LIST.getValue(), userId);if(CollectionUtil.isEmpty(userExams)){return null;}return userExams.stream().map(UserExam::getExamId).toList();}
    @Overridepublic TableDataInfo redisList(ExamQueryDTO examQueryDTO) {Long listSize = examCacheManager.getListSize(examQueryDTO.getType(),null);List<ExamVO> list;if(listSize==null||listSize==0){//说明缓存中没有数据,所以要先从数据库中获取数据,然后存入redislist = list(examQueryDTO);examCacheManager.refreshCache(examQueryDTO.getType(),null);listSize  = new PageInfo<>(list).getTotal();}else{//直接从redis中获取数据list = examCacheManager.getExamVOList(examQueryDTO,null);listSize = examCacheManager.getListSize(examQueryDTO.getType(),null);}assembleEnterExamVoList(list);return TableDataInfo.success(list, listSize);}private void assembleEnterExamVoList(List<ExamVO> list) {Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);List<Long> allUserExam = examCacheManager.getAllUserExam(userId);if(CollectionUtil.isEmpty(allUserExam)){return;}for(ExamVO examVO : list){if(allUserExam.contains(examVO.getExamId())){examVO.setEnter(true);}}}

这样就可以了

然后是前端开发

                            <div class="exam-button-container"><span class="exam-hash-entry"v-if="isOngoingAndUnregisteredCompetition(exam)">已开赛</span><span class="exam-hash-entry" v-if="isEntryAndNotStart(exam)">已报名</span><div v-if="isHistoryExam(exam)"><el-button class="exam-practice-button" type="primary" plain@click="goExam(exam)">竞赛练习</el-button><el-button class="exam-rank-button" type="primary" plain@click="togglePopover(exam.examId)">查看排名</el-button></div><el-button class="exam-start-button" type="primary" plain v-if="isStartExam(exam)"@click="goExam(exam)">开始答题</el-button><el-button class="exam-enter-button" type="primary" plain v-if="isCanEntry(exam)"@click="enterExam(exam.examId)">报名参赛</el-button></div>

给Exam.vue增加按钮

function isOngoingAndUnregisteredCompetition(exam) {const now = new Date(); //当前时间return new Date(exam.startTime) < now && new Date(exam.endTime) > now && !exam.enter
}function isEntryAndNotStart(exam) {const now = new Date();return new Date(exam.startTime) > now && exam.enter
}function isHistoryExam(exam) {const now = new Date();return new Date(exam.endTime) < now;
}function isStartExam(exam) {const now = new Date();return new Date(exam.startTime) < now && new Date(exam.endTime) > now && exam.enter;
}function isCanEntry(exam) {const now = new Date();return new Date(exam.startTime) > now && !exam.enter;
}const isLogin = ref(false)
async function checkLogin() {if (getToken()) {await getUserInfoService()isLogin.value = true}
}

然后是按钮是否显示的逻辑判断

然后是报名按钮

export function enterExamService(enterExamDTO) {return service({url: "/user/exam/enter",method: "post",data: enterExamDTO,});
}
async function enterExam(examId) {await checkLogin()if (!isLogin.value) {ElMessage.error('请先登录后报名参赛,谢谢')return}const examDto = ref({examId: examId})await enterExamService(examDto.value)ElMessage.success('您已报名成功,请按时参赛',)getExamList() //报名成功后刷新竞赛列表
}

这样就成功了

然后是我的竞赛列表页面

创建UserExam.vue
还有路由
在这里插入图片描述

function goMyExam(){router.push("/c-oj/home/user/exam")
}

然后是跳转按钮,这个跳转更新的是子页面

然后就是开发这个页面了

export function getMyExamListService(params) {return service({url: "/user/exam/list",method: "get",params,});
}
<template><div class="my-exam-page"><div class="exam-list-block"><div class="exam-list-header"><span class="ex-title">我的竞赛</span><span class="exam-list-back" @click="goBack">返回</span></div><div class="exam-list-content" v-for="(exam, index) in myExamList" :key="index"><img src="@/assets/ide/jingsai.png" class="image" /><div class="exam-content"><div class="title">{{ exam.title }}</div><div class="date"><span>比赛时间:</span> {{ exam.startTime }} ~ {{ exam.endTime }}</div></div><div><div class="exam-end-lable-list" v-if="isHistoryExam(exam)"><span class="exam-end-lable">已完赛</span><el-button class="exam-rank-lable" type="text" @click="togglePopover(exam.examId)">查看排名</el-button><el-button class="exam-rank-lable" type="text" @click="goHistoryExam(exam)">竞赛练习</el-button></div><div class="exam-status-lable exam-end-lable-list" v-else-if="isNotStart(exam)">未开赛</div><div class="exam-end-lable-list" v-else><el-button class="exam-rank-lable" type="text" plain @click="goExam(exam)">开始答题</el-button></div></div></div><div class="my-exam-pagination"><!-- 增加分页展示器 --><el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"v-model:current-page="params.pageNum" v-model:page-size="params.pageSize" :page-sizes="[5, 10, 15, 20]"@size-change="handleSizeChange" @current-change="handleCurrentChange" /></div></div></div>
</template><script setup>
import { reactive, ref } from 'vue'
import {getMyExamListService,
} from "@/apis/exam"
import router from '@/router'const myExamList = ref([]) //消息列表const total = ref(0)
const params = reactive({pageNum: 1,pageSize: 10,
})// 分页
function handleSizeChange(newSize) {params.pageNum = 1getMyExamList()
}function handleCurrentChange(newPage) {getMyExamList()
}//消息列表
async function getMyExamList() {const ref = await getMyExamListService(params.value)myExamList.value = ref.rowstotal.value = ref.total
}
getMyExamList()function isHistoryExam(exam) {const now = new Date();return new Date(exam.endTime) < now;
}const isNotStart = (exam) => {const now = new Date(); //当前时间return new Date(exam.startTime) > now;
}const goBack = () => {router.go(-1)
}</script><style lang="scss">
.my-exam-page {max-width: 1520px;margin: 0 auto;background-color: rgba(247, 247, 247, 1);position: relative;overflow: hidden;display: flex;justify-content: center;
}.exam-list-block {width: 100%;display: flex;flex-wrap: wrap;
}.exam-list-header {background-color: rgba(255, 255, 255, 1);border-radius: 10px;width: 100%;/* 设置宽度为100%以确保水平居中 */height: 60px;font-size: 25px;text-indent: 20px;display: flex;align-items: center;margin-top: 20px;.ex-title {font-family: PingFangSC, PingFang SC;font-weight: 600;font-size: 18px;color: #222222;}.exam-list-back {cursor: pointer;color: #999999;font-size: 15px;margin-left: auto;padding-right: 20px;}
}.exam-list-content {height: 110px;width: 100%;background: #FFFFFF;border-radius: 10px;margin: 0 auto;display: flex;align-items: center;margin-top: 20px;justify-content: space-between;cursor: pointer;&:hover {box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);}img {width: 50px;height: 50px;margin: 0 20px;}
}.exam-list-content .exam-content {max-width: calc(100% - 320px);width: 100%;
}.exam-list-content .title {font-weight: bold;font-size: 16px;margin-bottom: 10px;
}.exam-list-content .date {font-weight: 400;color: #000;line-height: 20px;margin-left: auto;font-size: 14px;span {color: #999;}/* 将日期推至行尾 */
}.exam-list-content .exam-status-lable {color: #999999;
}.exam-end-lable-list {padding-right: 20px;min-width: 228px;text-align: right;.exam-end-lable {margin-right: 10px;font-family: MicrosoftYaHei;font-size: 16px;color: #999999;}.exam-rank-lable {font-size: 16px;}}.my-exam-pagination {width: 100%;display: flex;justify-content: flex-end;padding-bottom: 20px;
}
</style>

在这里插入图片描述
这样就可以了
但是我们这个我的竞赛页面不想显示页面的图片,该怎么做呢
可以给页面增加变量

    {path: '/c-oj/home',name: 'home',component: () => import('@/views/Home.vue'),children: [{path: 'exam',name: 'exam',component: () => import('@/views/Exam.vue'),meta: { showNavbar: true }},{path: 'question',name: 'question',component: () => import('@/views/Question.vue'),meta: { showNavbar: true }},{path: 'user/exam',name: 'userExam',component: () => import('@/views/UserExam.vue'),meta: { showNavbar: false}}]},

在Home页面中

    <div  v-if="$route.meta.showNavbar" ><img src="@/assets/images/log-banner.png" class="banner-img"></div>

这样就可以了

在这里插入图片描述

2. 题目列表功能

先分析一下 redis
题目可以有一个list
key为question:list
value为questionId
然后是每个竞赛详细信息的redis存储
key为question:questionId
value为json详细信息
但是有一个问题,就是redis虽然支持分页搜索,但是不支持模糊搜索那种搜索
但是也可以拿出所有详细题目信息缓存,然后挨个进行模糊搜索–》拿出所有缓存就很性能差了,而且还有所有在遍历一次
第二个办法是把key改为要模糊搜索的字段,比如key含有标题这种要模糊查询的字段,但是中文做key吗,挺别扭的,而且这个办法也不好

业界的redis模糊查询我们用的是elasticSearch

2.1 elasticSearch基本知识

官网
学习官网

我们用的是8.5版本

ElasticSearch,简称ES(后⽂将直接使⽤这⼀简称),是⼀款卓越的开源分布式搜索引擎。其独特之处在于其近乎实时的数据检索能⼒,为⽤⼾提供了迅速、⾼效的信息查询体验。

ES解决什么问题
全⽂检索(全部字段)、模糊查询(搜索)、数据分析(提供分析语法,例如聚合)

在这里插入图片描述
倒排索引(Inverted Index)
倒排索引,也被称为反向索引或逆向索引,是⼀种索引数据的⽅法。与正排索引不同。倒排索引是按照⽂档中的词汇(关键词)来组织。也就是说在倒排索引中,索引的键是⽂档集合中出现过的每个独特词汇或关键词,索引的值是包含该关键词的所有⽂档的标识符(如⽂档ID),以及可选的额外信
息。

ES采取的是倒排索引

索引关键字                对应数据序号
⽩蛇                     1234
⼤战                     2
告别                     34
法海 					2
⻘蛇						3
许仙 					4

在这里插入图片描述
Index 索引
索引,具有相同结构的⽂档集合,类似于关系型数据库的数据库实例(6.0.0版本type废弃后,索引的概念下降到等同于数据库表的级别)。⼀个集群⾥可以定义多个索引,如客⼾信息索引、商品分类索引、商品索引、订单索引、评论索引等等,分别定义⾃⼰的数据结构。索引命名要求全部使⽤⼩写,建⽴索引、搜索、更新、删除操作都需要⽤到索引名称。

type 类型
类型,原本是在索引(Index)内进⾏的逻辑细分,但后来发现企业研发为了增强可阅读性和可维护性,制订的规范约束,同⼀个索引下很少还会再使⽤type进⾏逻辑拆分(如同⼀个索引下既有订单数据,⼜有评论数据),因⽽在6.0.0版本之后,此定义废弃

每个索引只有一中type类型,但是有很多个

Document ⽂档
⼀个⽂档是⼀个可被索引的基础信息单元,Document(⽂档)是JSON格式的,Document 就像是MySQL 中某个 Table ⾥⾯每⼀⾏的数据,Document中可以包含多个字段,每个字段可以是⽂本、数字、⽇期等类型

Field 字段:
字段是⽂档中的⼀个元素或属性,每个字段都有⼀个数据类型,如字符串、整数、⽇期等。

Mapping 映射
Mapping是ES中的⼀个很重要的内容,它类似于传统关系型数据中table的schema(定义了数据库中的数据如何组织,包括表的结构、字段的数据类型、键的设置(如主键、外键)等),⽤于定义⼀个索引的数据的结构(mapping中主要包括字段名、字段数据类型和字段索引类型。)。 在ES中,我们可以⼿动创建mapping,也可以采⽤默认创建⽅式。在默认配置下,ES可以根据插⼊的数据⾃动地创
建mapping。

2.2 elasticSearch使用

拉取es镜像:

docker pull elasticsearch:8.5.3

创建⽹络

docker network create oj-network

为什么要配置网络呢
因为Kibana要操作es,所以配置同一个网络的话,就可以链接了

输入docker network ls
就可以查看创建的网络oj-network了
启动es:

docker run -d --name oj-es-dev -e "ES_JAVA_OPTS=-Xms256m -Xmx256m" -e "discovery.type=single-node" -v D:\spring-project\ck-oj\deploy\dev\elasticSearch\es-plugins:/usr/share/elasticsearch/plugins -e "xpack.security.enabled=false" --privileged --network oj-network -p 9200:9200 -p 9300:9300 elasticsearch:8.5.3

–network oj-network是配置的网络
-v D:\spring-project\ck-oj\deploy\dev\elasticSearch\es-plugins:/usr/share/elasticsearch/plugins是配置挂载卷,将容器映射到本地
----》这样在plugins新增插件就可以在本地操作了

-e "xpack.security.enabled=false"是关闭身份认证
-e "discovery.type=single-node"是单体模式
-e "ES_JAVA_OPTS=-Xms256m -Xmx256m"是配置最大和最小内存,防止es占完了

要先启动es,才能启动Kibana

访问:http://localhost:9200/,验证启动成功。
在这里插入图片描述

2.2 Kibana

Kibana是ElasticSearch的数据可视化和实时分析的⼯具。通过Kibana,⽤⼾可以搜索、查看和与存储在Elasticsearch索引中的数据进⾏交互,执⾏⾼级数据分析,并通过各种图表、表格和地图将数据可视化。
就是ES的可视化工具
拉取kibana镜像

docker pull kibana:8.5.3

与ES版本一样

启动kibana容器

docker run -d --name oj-kibana-dev -e "ELASTICSEARCH_HOSTS=http://oj-es-dev:9200" -e "I18N_LOCALE=zh-CN" -p15601:5601 --net=oj-network kibana:8.5.3

-e "I18N_LOCALE=zh-CN"是配置中文页面
-e "ELASTICSEARCH_HOSTS=http://oj-esdev:9200"是配置es地址

访问:http://localhost:15601/,验证启动成功。

在这里插入图片描述
点击自己浏览

在这里插入图片描述
点击开发工具

在这里插入图片描述
左侧是命令,右侧是结果

3. es命令

3.1 PUT增加

PUT /employee/_doc/1
{"first_name" : "John","last_name" : "Smith","age" : 25,"about" : "I love to go rock climbing","interests": [ "sports", "music" ]
}PUT /employee/_doc/2
{"first_name" : "Jane","last_name" : "Smith","age" : 32,"about" : "I like to collect rock albums","interests": [ "music" ]
}PUT /employee/_doc/3
{"first_name" : "Douglas","last_name" : "Fir","age" : 35,"about": "I like to build cabinets","interests": [ "forestry" ]
}

put就是增加的意思
PUT /employee/_doc/3的意思就是
先会增加一个employee索引,1表示新增第一条数据

employee:
表示索引名称(类似数据库中的 “表”),是存储文档的逻辑容器。
这里表示要操作的是名为 employee 的索引,如果该索引不存在,Elasticsearch 会自动创建它(基于默认配置)。
_doc:
表示文档类型(在 Elasticsearch 7.x 及以上版本中,这是默认且唯一的文档类型)。
历史上,一个索引可以有多个文档类型(类似表中的不同 “子表”),但 7.x 后已废弃多类型,统一使用 _doc 作为类型名。
它仅作为路径中的固定标识,无实际业务含义。
3:
表示文档 ID(类似数据库中的 “主键”),是当前文档的唯一标识。
通过这个 ID 可以精确操作该文档(如查询、修改、删除)。
如果省略 ID(如 PUT /employee/_doc),Elasticsearch 会自动生成一个随机字符串作为 ID。

向 employee 索引中,添加或更新 ID 为 3 的文档(如果 ID 为 3 的文档已存在,则会覆盖更新;不存在则创建)。

3.2 GET查询

GET /employee/_search

该请求默认会查询 employee 索引下的所有文档(类似 SQL 的 SELECT * FROM employee),返回的结果会包含匹配的文档列表、总命中数、排序结果等元数据。

_search:Elasticsearch 的搜索专用端点,表示对前面指定的 employee 索引执行 “搜索 / 查询” 操作。
在这里插入图片描述
这样就成功了

GET /employee/_doc/3

这个表示查询id为3的文档

在这里插入图片描述

3.3 DELETE删除

DELETE /employee/_doc/3

删除指定数据

DELETE /employee

直接删除索引

3.4 POST修改

POST /employee/_update/3{"doc" : { "last_name" : "mark"}}

这个就是修改第三条数据的last_name字段

3.5 分词器

根据前⾯学习的倒排索引的概念。倒排索引是按照⽂档中的词汇(关键词)来组织的,索引的键是⽂档集合中出现过的每个独特词汇或关键词。那es是怎么将这些关键词提取出来的呢?这其实就是es中的分词器在起着作⽤,它负责将⽂本切分成⼀个个有意义的词语,以建⽴索引或进⾏搜索和分析。
我们的业务中通常使⽤的是中⽂分词,es的中⽂分词默认会将中⽂词每个字看成⼀个词⽐如:“我想吃⾁夹馍”会被分为”我”,”想”,”吃”,”⾁” ,”夹”,”馍” 这显然是不太符
合⽤⼾的使⽤习惯,所以我们需要安装中⽂分词器ik,来讲中⽂内容分解成更加符合⽤⼾使⽤的关键
字。

 GET _analyze {"text": "我想吃⾁夹馍"}

这个代码的意思就是对我想吃⾁夹馍进分词

在这里插入图片描述
我们发现确实对中文就只能一个一个分词

3.6 安装ik分词器

下载官网

下载到D:\spring-project\ck-oj\deploy\dev\elasticSearch\es-plugins这个目录
就是绑定卷的目录,就是映射的那个目录
在这里插入图片描述
这样就可以了

如果版本不是8.5.3,那么就要改文件名为8.5.3
然后是plugin-descriptor.properties里面要改版本为8.5.3

version=8.5.3
elasticsearch.version=8.5.3

然后是重启es容器

 GET _cat/plugins

这个命令就可以知道我们安装的插件了

在这里插入图片描述

3.7 分词模式

ik分词器提供了两种分词模式:ik_smart 和 ik_max_word,其中 ik_smart 模式会尽量保持⻓词,尽可能地保留词语的完整性,提⾼搜索的准确性,⽽ ik_max_word 模式则会尽可能多地切分出词汇。

 GET _analyze {"text": "我想吃肉夹馍","analyzer": "ik_smart"}

这个就是指定ik分词器的什么分词模式了
在这里插入图片描述

GET _analyze 
{"text": "我想吃⾁夹馍","analyzer": "ik_max_word"
}

在这里插入图片描述

3.8 项目引入

先创建一个公共包oj-common-elasticSearch

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

这个包是空的,什么代码都没写,万一后面会写代码呢,而且这是一个组件,所以我们要低耦合

然后是es配置
在这里插入图片描述

直接在friend下引入oj-common-elasticSearch,然后配置

总结

http://www.xdnf.cn/news/1339129.html

相关文章:

  • javaweb开发笔记—— 前端工程化
  • Spring Boot 集成 Redis 发布订阅实现消息通信
  • 计算机网络技术学习-day6《三层交换机配置》
  • 01 网络信息内容安全--绪论
  • 2025.7.19卡码刷题-回溯算法-组合
  • Web 安全之 HTTP 响应截断攻击详解
  • 数据结构初阶:排序算法(三)归并排序、计数排序
  • 【数据结构】深入解析选择排序与堆排序:从基础到高效实现的完全指南
  • 深度卷积神经网络AlexNet
  • openEuler系统中r如何将docker安装在指定目录
  • 神经网络中 标量求导和向量求导
  • 如何通过传感器选型优化,为设备寿命 “续航”?
  • 机器学习6
  • RootDB:一款开源免费的Web报表工具
  • 0821 sqlite3_get_table函数(数据库函数的补充)
  • Vue.js 中使用 Highcharts 构建响应式图表 - 综合指南
  • 遥感机器学习入门实战教程|Sklearn案例⑤:集成学习方法全览
  • Python学习-- 数据库和MySQL入门
  • CentOS 7常用国内源配置:阿里云、腾讯云、华为云、清华源
  • pycharm编译器如何快速掌握一个新模块的使用方法
  • JeeSite 快速开发平台:全能企业级快速开发解决方案
  • 【图像算法 - 20】慧眼识病:基于深度学习与OpenCV的植物叶子疾病智能识别系统
  • Python-Pandas GroupBy 进阶与透视表学习
  • 中国之路 向善而行 第三届全国自驾露营旅游发展大会在阿拉善启幕
  • Android14内核调试 - boot vendor_boot
  • 06高级语言逻辑结构到汇编语言之逻辑结构转换 for (...; ...; ...)
  • uni-app:实现文本框的自动换行
  • Android - 资源类型 MINE Type
  • 教育场景下禁用html5播放器拖动进度条的例子
  • 医疗信息化实战:引领医疗行业数字化转型实践