深入流行推荐引擎3:Spotify音乐推荐系统

深入流行推荐引擎3:Spotify音乐推荐系统

  • Spotify音乐推荐系统
    • 通过矩阵分解发现每周(Discover Weekly via Matrix Factorization)
      • Discover Weekly 如何运作?(How Discover Weekly Works?)
      • 矩阵分解(Matrix Factorization)
      • 交替最小二乘法(Alternating Least Squares)
    • 用于音乐发现的 RNN(RNNs for Music Discovery)
    • 使用强化学习的播放列表推荐(Playlist Recommendation Using Reinforcement Learning)
      • 概述(Overview)
      • 世界模型设计(World Model Design)
      • 行动负责人 DQN 方法(Action Head DAN Approach)
  • 总结
  • 参考

互联网彻底改变了人们发现、消费和与音乐互动的方式。摆脱了 DVD、磁带和录音带的麻烦,现在可以通过流媒体平台(例如 Spotify、Amazon Music、Apple Music 等)随时随地收听音乐。

Spotify 在全球拥有超过 2 亿用户,已成为音频流媒体服务的行业领导者。它们已经从音乐扩展到播客和有声读物,吸引了具有不同偏好和兴趣的用户。

Spotify 最先进的音乐推荐引擎,可根据用户的兴趣、偏好和当前心情向用户推荐音乐或播客。 推荐系统在提供良好的用户体验方面发挥了重要作用,这为 Spotify 带来了如此大的成功。

这篇博客将涵盖 Spotify 推荐的几个方面(例如,音乐推荐、播放列表推荐)以及它们在幕后的工作原理。
推荐引擎3 部分系列的最后一节课深入探讨流行音乐推荐引擎 :

  1. 推荐系统基础
  2. 亚马逊产品推荐系统
  3. YouTube 视频推荐系统
  4. Spotify 音乐推荐系统(本文)

Spotify音乐推荐系统

尽管音乐推荐的任务可能感觉类似于电影推荐(正如在 Netflix 推荐系统中学到的那样),但两者的工作方式存在一些差异:

  • 与电影相比,歌曲目录相当大。同一首歌可以有多种变体(例如低保真、混音、慢速、混响、不同的歌手、乐队等)。但是,电影不太可能有变化。
  • 歌曲具有重复的消费模式,因为您可能会更频繁地听自己喜欢的歌曲。然而电影并非如此。
  • 与电影相比,音乐更小众。有些人可能喜欢民谣、1990 年代的歌曲、流行或摇滚,或者某个乐队或歌手。
  • 电影的收视率更为明确。但是对于歌曲反馈隐藏在用户的流模式中。

Spotify 拥有数百万首歌曲、用户口味、流媒体音乐和用户生成的播放列表的数据。现在将研究Spotify 如何使用这些数据源和先进的机器学习技术来解决音乐推荐问题。

通过矩阵分解发现每周(Discover Weekly via Matrix Factorization)

Discover Weekly 如何运作?(How Discover Weekly Works?)

Spotify 的 Discover Weekly是一个算法生成的播放列表,每周一发布,为听众提供定制的、精心策划的音乐推荐。在幕后Discover Weekly使用协作过滤来利用用户收听历史和具有相似历史的用户喜欢的歌曲。回想一下,协作筛选会根据具有相似兴趣的其他用户喜欢的项目向用户推荐项目。

Spotify 还通过将用户经常听的音乐分组来建立品味档案。这些集群不是基于明确的属性(例如,流派、艺术家等),而是基于歌曲的作曲相似性。

除了明确的反馈,如库保存和“从广播中点赞”,Spotify还依靠隐式反馈(例如歌曲重复、跳过歌曲、歌曲点击等)来训练他们的算法。它使用基于矩阵分解的方法,将用户项目评分矩阵分解为两个部分。第一部分根据潜在因素来描述用户,每个因素的权重不同。第二部分描述歌曲,艺术家与用户部分处于同一潜在空间。

矩阵分解(Matrix Factorization)

为了清楚地理解,假设有:

  • 一组 N N N 用户 U = ( u 1 , u 2 , … , u N ) U = (u_1, u_2,\dots,u_N) U=(u1,u2,,uN)
  • 一组 M M M 歌曲项目, I = ( i 1 , i 2 , … , i M ) I = (i_1,i_2,\dots, i_M) I=(i1,i2,,iM)
  • 用户项目观察矩阵 R = ( r u i ) n × m ) R = (r_{ui})_{n \times m}) R=(rui)n×m) 哪里 r u i ≥ 0 r_{ui} \geq 0 rui0 表示用户次数 u u u 与项目交互 i i i

用户-项目观察矩阵的设计取决于问题的性质。与隐性反馈相比可以选择更多地权衡显性反馈。此外,人们可能会选择将较新的用户流的权重高于较旧的流,因为用户的口味可能会随着时间的推移而略有变化。

请注意,矩阵 R型 通常是稀疏的,因为大多数用户只与目录中有限数量的项目进行交互。对于用户-项目组合,其中用户 u u u 尚未与项目交互 i i i r u i r_{ui} rui 设置为零。矩阵分解旨在近似于这些 r u i r_{ui} rui 未知用户-项目组合的值。

该方法分解观察矩阵 R R R 为两个矩阵:
用户矩阵: X N × f = [ x 1 , x 2 , … , x N ] X_{N \times f} = [x_1, x_2,\dots,x_N] XN×f=[x1,x2,,xN],通过未定义的潜在因素描述N个用户中的每一个
项目矩阵: Y M × f = [ y 1 , y 2 , … , y M ] Y_{M \times f} = [y_1, y_2,\dots,y_M] YM×f=[y1,y2,,yM],通过同样未定义的潜在因素描述M歌曲。
换句话说,
R ≈ X Y T R \approx XY^T RXYT
r u i = < x u , y i > r_{ui}=<x_u,y_i> rui=<xu,yi>
其中 < . , . > <.,.> <.,.>表示向量内积运算。

为了学习 X X X Y Y Y 的矩阵,Spotify最小化了未定义的观测值和未定义预测值之间的以下RMSE(均方根误差 Root Mean Square Error)损失
未定义的
m i n x , y ∑ u , i ( r u i − x u T y i − b u − b i ) 2 − λ ( ∑ u ∣ ∣ x u ∣ ∣ 2 + ∑ i ∣ ∣ y i ∣ ∣ 2 ) min_{x,y}\textstyle\sum_{u,i} (r_{ui}-x_u^Ty_i-b_u-b_i)^2-\lambda(\textstyle\sum_{u} ||x_u||^2+\textstyle\sum_{i} ||y_i||^2) minx,yu,i(ruixuTyibubi)2λ(u∣∣xu2+i∣∣yi2)

其中 λ \lambda λ 是控制正则化的超参数: b i b_i bi b u b_u bu 都是项 i i i 和 用户 u u u 的偏差。

交替最小二乘法(Alternating Least Squares)

X X X Y Y Y 的矩阵使用如下交替最小二乘算法进行优化:

  1. 随机初始化 X X X Y Y Y
  2. 修复未定义的项目向量 y i y_i yi ,并为未定义的用户向量 x u x_u xu 求解。取上述损失函数相对于未定义的用户向量 x u x_u xu 的导数,并将其设置为零,这给出
    x u = ( Y T C u Y + λ I ) − 1 Y T R ( u ) x_u=(Y^TC^uY+\lambda I)^{-1}Y^TR(u) xu=(YTCuY+λI)1YTR(u)
    其中 C u C_u Cu 是常数乘法器,undefine是观测矩阵R的未定义行型。
  3. 固定用户向量并求解项目向量(使用与步骤2相同的过程)。取上述损失函数相对于未定义的项向量的导数,并将其设置为零,这给出
    y i = ( X T C i X + λ I ) − 1 X T R ( i ) y_i=(X^TC^iX+\lambda I)^{-1}X^TR(i) yi=(XTCiX+λI)1XTR(i)
    其中 C i C^i Ci 是常数乘法器,$ R ( i ) R(i) R(i) 是观测矩阵R的第i个 i t h i^{th} ith 义列型。
  4. 重复直到收敛。

用于音乐发现的 RNN(RNNs for Music Discovery)

在音乐消费中,用户行为不断变化,可以用不同艺术家和流派的时间轨迹来描述。例如,想象一个用户听了一段时间艺术家的专辑,然后过渡到下一张专辑或同一音乐流派的汇编播放列表。Spotify使用递归神经网络(RNN)来利用用户的收听模式,将用户表示为其物品消费的函数。

RNN是最先进的神经网络,它处理可变长度的时间序列数据来学习丰富的项目表示。像任何神经网络一样,它们可以通过基于梯度的优化来学习。它们已被广泛用于对文本、语音和音频的非线性时间动态进行建模,因此是Spotify音乐发现问题的一个很好的候选者。

通常在 RNN 中,在每个时间步长 t t t、输入 x t x_t xt 用于更新隐藏的表示 h t − 1 h_{t-1} ht1 h t h_t ht 通过非线性映射。 同样,在每个时间步长,RNN 都会产生一个输出 y t y_t yt 作为输入的非线性函数 x t x_t xt 和以前的隐藏状态 h t − 1 h_{t-1} ht1

如何训练 RNN 来预测用户将来可能听的歌曲。主要有3个步骤:

  1. 管道的构建块是歌曲矢量表示,这些表示已经从目录中学习。为了学习这些矢量表示,Spotify 在目录中排名前 N 位的最流行歌曲中使用了 Google 的 Word2vec 套件。

    更具体地说,他们使用具有负采样的连续词袋 (CBoW) 算法。作为 Word2vec 算法的输入,它们采用用户创建的歌曲播放列表。在这种情况下,每个播放列表都被视为歌曲的有序“文档”。 通过以窗口方式扫描所有播放列表,Word2vec 将为每首歌曲学习具有固定维度的分布式矢量表示。

  2. RNN 输出一个味觉向量,它是从用户收听历史中学习的歌曲向量的函数,因此可以被视为用户音乐品味的表示。味觉向量应捕捉聆听行为如何随时间变化。因此RNN 预测用户将来可能会听的歌曲。为了对此进行建模,RNN 首先采用 N N N 特定用户的连续歌曲 u u u 已经听过并尝试预测下一首歌的向量。换言之,

s ^ n + 1 = RNN ( s 1 , s 2 , s 3 , … , s n ) \hat{s}_{n+1} = \text{RNN}(s_1, s_2,s_3,\dots,s_n) s^n+1=RNN(s1,s2,s3,,sn)

其中 s i s_i si 是用户在 i th i^\text{th} ith timestamp 和 s ^ n + 1 \hat{s}_{n+1} s^n+1 是未来时间戳的预测味觉向量 n + 1 n+1 n+1

作为损失函数,它们最小化了预测味觉向量之间的 L2 距离 s ^ n + 1 \hat{s}_{n+1} s^n+1 和未来的歌曲矢量 s n + 1 s_{n+1} sn+1

  1. 这些口味矢量现在可以生成歌曲推荐。由于已经训练了 RNN 来预测与用户未来可能播放的歌曲(以 L2 距离为单位)接近的味觉向量,因此可以在该向量空间中查询附近的歌曲以生成新的歌曲推荐。

为了促进高效采样,Spotify 利用了 Annoy 树,这是一种数据结构,它使用局部敏感的哈希随机投影技术将向量空间迭代划分为多个区域,从而实现近似最近邻查询。

使用强化学习的播放列表推荐(Playlist Recommendation Using Reinforcement Learning)

概述(Overview)

协作过滤方法的一个缺点是,它们依赖于显式或隐式反馈信号来判断用户是否喜欢播放列表。 因此他们往往难以考虑其他重要因素(例如,声学连贯性、聆听会话的背景以及最佳项目序列的存在)。这可能会导致离线指标与用户满意度指标(想要优化的指标)不匹配。

例如,协作过滤可以建议评分较高但包含成人和儿童音乐的混合播放列表,这不会带来良好的用户满意度。因此,播放列表生成是一项艰巨的任务。

强化学习 (RL) 是一个高级机器学习领域,它不需要明确的反馈信号,而是可以通过直接与用户交互来学习。因此,RL代理可以交互并学习重要的播放列表生成方面,以提高用户满意度指标。

RL 问题包括一个由状态、动作空间和奖励以及 RL 代理组成的环境。RL 代理查看环境的当前状态,并根据学习的策略建议操作。然后,环境会根据操作将其当前状态更改为另一种状态。除了更改其状态外,环境还会返回与此操作关联的奖励。然后,RL 代理会消耗此奖励,以优化其策略以获得更高的奖励。

现在,为了将RL应用于播放列表生成,问题陈述应该表述为马尔可夫决策过程,其中:

  • 状态对总结用户侦听会话的上下文信息进行编码
  • 操作空间表示所有可能的播放列表的空间
  • 奖励是期望的用户满意度指标

操作空间 A \mathcal{A} A 是所有可能的播放列表的空间,这在组合上是复杂的。例如,从 1000 个候选曲目池中生成包含 30 首曲目的播放列表会生成一个操作空间,其中大约 1 0 89 10^{89} 1089 可能性,使现成的RL算法不适用。为了解决这个问题,建议一次只播放一首曲目(来自候选池),而不是完整的播放列表。

世界模型设计(World Model Design)

为了准确地学习生成播放列表,RL 代理应该从模拟实际用户收听会话的环境中学习。为此Spotify提出了一种世界模型设计,该设计利用历史数据(例如,曲目特征和收听历史)来模拟过渡函数,该转换函数根据特定动作产生新状态及其奖励。

在模拟实际的用户收听会话时,世界模型还应该能够启动用户会话,跟踪所有收听的曲目,并终止会话。为了预测与操作相关的奖励,世界模型应用了一个经过训练的用户模型,该模型可以准确预测用户对项目(在例子中为音轨)的反应。 这种模型是在通过实验从真实用户那里收集的监督数据上训练的。

训练数据是通过随机随机播放来自各种收听会话的歌曲并收集用户完成的曲目(如果流式传输)的百分比及其在收听会话中的位置来生成的。然后将这些数据与以下数据相结合:

  • 上下文特征: 用户信息(例如,与个人兴趣和过去互动相关的特征)
  • 项目功能: 与用户正在与之交互的内容相关的信息

用户模型采用用户的上下文特征 在 和项目特征 i t i_t it 的轨道 t th t^\text{th} tth 在侦听会话中的位置以预测以下三个用户响应:

  • 完成候选人跟踪
  • 跳过候选人轨道
  • 收听曲目超过特定秒数(先验指定)

从数学上讲,用户模型采用以下形式:

p ( y t ∣ u , i t ) = f ( u , i t ) p(y_t | u,i_t)= f(u, i_t) p(ytu,it)=f(u,it)

f f f 是一种深度神经网络,它采用上下文和项目特征来预测用户响应。然后,每个用户响应都映射到一个奖励,供 RL 代理在训练期间使用。

行动负责人 DQN 方法(Action Head DAN Approach)

候选曲目池会随着时间的推移而不断变化,因为它们取决于用户的收听历史和品味。这使得整个操作空间是动态的。

为了解决这个问题,Spotify 提出了一个 Action Head Deep-Q-Network (AH-DQN) 代理来采取行动 a a a(例如,要推荐的曲目)和当前状态 s s s (即用户监听会话)作为推荐质量的输入和输出 Q ( s , a ) Q(s,a) Q(s,a)

AH-DQN 代理使用深度神经网络来输出推荐质量 Q ( s , a ) Q(s,a) Q(s,a)。根据每个磁道的 Q Q Q 值,智能体选择具有最高值的磁道。环境返回的奖励用于指示操作的好坏,并更新 Q-相应地网络。

总结

Spotify 是领先的音频流媒体平台,提供个性化的音乐和播客推荐。在这篇博客中了解了 Spotify 如何使用各种数据源和机器学习技术来创建不同的推荐(例如,每周发现、播放列表推荐和音乐发现(Discover Weekly, playlist recommendation, and music discovery)。

  • 通过矩阵分解发现每周(Discover Weekly via Matrix Factorization) :Spotify 如何使用矩阵分解创建符合用户口味的每周歌曲播放列表,该矩阵将用户与歌曲交互的大型矩阵分解为较小的用户和歌曲特征矩阵。
  • 用于音乐发现的 RNN(RNNs for Music Discovery) : Spotify 如何使用递归神经网络 (RNN) 来模拟音乐聆听的顺序性质,并生成与用户先前选择相似的新歌曲。
  • 使用强化学习的播放列表推荐(Playlist Recommendation Using Reinforcement Learning : Spotify 如何使用强化学习 (Reinforcement Learing RL) 创建平衡探索和利用的播放列表(即查找用户可能喜欢的新歌曲并播放用户已经喜欢的歌曲)。介绍了世界模型(学习模拟用户行为和偏好的神经网络(simulate the user’s behavior and preferences))和动作头(学习为播放列表选择最佳歌曲的深度 Q 网络 (DQN deep Q-network))的概念。

然而,这只是冰山一角,因为还有更多 Spotify 推荐系统。通过探索各种推荐引擎(例如 Netflix、LinkedIn、Amazon 和 YouTube 推荐系统)的课程深入了解推荐系统。深入了解所使用的技术(例如,文本挖掘、K 最近邻、聚类、矩阵分解和神经网络(text mining, K-nearest neighbor, clustering, matrix factorization, and neural networks)。

参考

  • https://pyimagesearch.com/2023/10/30/spotify-music-recommendation-systems/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/165983.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【IPC】 共享内存

1、概述 共享内存允许两个或者多个进程共享给定的存储区域。 共享内存的特点 1、 共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了数据&#xff0c;共享这个内存区域的所有进程就可以立刻看到 其中的内容。 2、使用共享内存要注意的是多个进程…

使用持久卷部署 WordPress 和 MySQL

&#x1f5d3;️实验环境 OS名称Microsoft Windows 11 家庭中文版系统类型x64-based PCDocker版本Docker version 24.0.6, build ed223bcminikube版本v1.32.0 &#x1f587;️创建 kustomization.yaml 你可以通过 kustomization.yaml 中的生成器创建一个 Secret存储密码或密…

Android问题笔记四十六:解决open failed: EACCES (Permission denied) 问题

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列点击跳转>ChatGPT和AIGC &#x1f449;关于作者 专…

Jmeter性能实战之分布式压测

JMeter分布式执行原理 1、JMeter分布式测试时&#xff0c;选择其中一台作为调度机(master)&#xff0c;其它机器作为执行机(slave)。 2、执行时&#xff0c;master会把脚本发送到每台slave上&#xff0c;slave 拿到脚本后就开始执行&#xff0c;slave执行时不需要启动GUI&…

深度学习YOLO抽烟行为检测 - python opencv 计算机竞赛

文章目录 1 前言1 课题背景2 实现效果3 Yolov5算法3.1 简介3.2 相关技术 4 数据集处理及实验5 部分核心代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习YOLO抽烟行为检测 该项目较为新颖&#xff0c;适合作为竞赛课…

Parity Game——种类并查集、权值并查集、离散化

题目描述 思路 怎么得到这个序列中每一段的关系&#xff1f; 我们可以把这个只包含0和1的序列看作一个数组&#xff0c;0表示当前位置为0&#xff0c;1表示当前位置为1&#xff0c;利用前缀和的性质可以知道某一段中所包含的1的数量sum1 a[r] - a[l-1] 如果sum1为偶数&…

Rust开发——切片(slice)类型

1、什么是切片 在 Rust 中&#xff0c;切片&#xff08;slice&#xff09;是一种基本类型和序列类型。在 Rust 官方文档中&#xff0c;切片被定义为“对连续序列的动态大小视图”。 但在rust的Github 源码中切片被定义如下&#xff1a; 切片是对一块内存的视图&#xff0c;表…

如何隐藏Selenium特征实现自动化网页采集

Selenium是一个流行的自动化网页测试工具&#xff0c;可以通过模拟用户在Chrome浏览器中的操作来完成网站的测试。然而&#xff0c;有些网站会检测浏览器是否由Selenium驱动&#xff0c;如果是&#xff0c;就会返回错误的结果或拒绝访问。为了避免这种情况&#xff0c;我们需要…

多线程编程

1 线程的使用 1.1 为什么要使用多线程 在编写代码时&#xff0c;是否会遇到以下的场景会感觉到难以下手&#xff1f; 要做 2 件事&#xff0c;一件需要阻塞等待&#xff0c;另一件需要实时进行。例如播放器&#xff1a;一边在屏幕上播放视频&#xff0c;一边在等待用户的按…

中间件安全:Apache 目录穿透.(CVE-2021-41773)

中间件安全&#xff1a;Apache 目录穿透.&#xff08;CVE-2021-41773&#xff09; Apache 的 2.4.49、2.4.50 版本 对路径规范化所做的更改中存在一个路径穿越漏洞&#xff0c;攻击者可利用该漏洞读取到Web目录外的其他文件&#xff0c;如系统配置文件、网站源码等&#xff0c…

Polygon zkEVM协议治理、升级及其流程

1. 引言 随着Polygon社区开发者和内部团队的测试深入&#xff0c;当前版本的Polygon zkEVM不可避免地需更新和某些升级。 为激励开发者对Polygon zkEVM做battle-test&#xff0c;已启动了bug-bounty&#xff1a; Rewards by Threat Level 由于zk-Rollup生态系统还处于萌芽阶…

算法设计与分析复习--贪心(二)

文章目录 上一篇哈夫曼编码单源最短路最小生成树Kruskal算法Prim算法 多机调度问题下一篇 上一篇 算法设计与分析复习–贪心&#xff08;一&#xff09; 哈夫曼编码 产生这种前缀码的方式称为哈夫曼树 哈夫曼树相关习题AcWing 148. 合并果子 #include <iostream> #inc…

LDO线性稳压器要不要并联二极管?

昨天介绍过了LDO是什么东西&#xff0c;那么对于它的应用场景是怎么的呢&#xff1f;LDO要不要并联二极管呢&#xff1f; 一般来说&#xff0c;LDO是不需要并联二极管的。 看下图第一个是典型电路&#xff0c;第二个是带可调节电压功能的LDO典型电路&#xff0c;从图里就可以…

设计模式-组合模式-笔记

“数据结构”模式 常常有一些组件在内部具有特定的数据结构&#xff0c;如果让客户程序依赖这些特定数据结构&#xff0c;将极大地破坏组件的复用。这时候&#xff0c;将这些特定数据结构封装在内部&#xff0c;在外部提供统一的接口&#xff0c;来实现与特定数据结构无关的访…

一起Talk Android吧(第五百五十四回:分享一个Retorfit使用错误的案例)

文章目录 1. 案例场景2. 案例现象3. 原因分析和解决方案3.1 原因分析3.2 解决方案4. 经验总结各位看官们大家好,上一回中咱们说的例子是"解析Retrofit返回的数据",本章回中将分享一个 Retrofit使用错误的案例。闲话休提,言归正转,让我们一起Talk Android吧! 1. …

三层交换机实现不同VLAN间通讯

默认时&#xff0c;同一个VLAN中的主机才能彼此通信&#xff0c;那么交换机上的VLAN用户之间如何通信&#xff1f; 要实现VLAN之间用户的通信&#xff0c;就必须借助路由器或三层交换机来完成。 下面以三层交换机为例子说明&#xff1a; 注意&#xff1a; 1.交换机与三层交换…

HWS-CTF-第七期山大站-inverse

文章目录 inversemainworkread_intread_n 思路onegadget exp 第一次真正意义上独立在比赛中做出题目来了&#xff0c;距离真正意义接触CTF-PWN差不多正好两个月。但由于不知道靶场要自己开而且端口每次自己打开会改&#xff0c;交flag稍微晚了些&#xff08;我太菜了&#xff0…

Java中锁的深入理解

目录 对象头的理解 Monitor&#xff08;锁&#xff09; 锁类型 偏向锁 偏向锁的优化机制 轻量级锁 重量级锁 对象头的理解 在32位Java虚拟机中普通对象的对象头是占用8个字节&#xff0c;其中4个字节为Mark Word。用来存储对象的哈希值&#xff0c;对象创建后在JVM中的…

【顺序表的实现】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 数据结构相关概念 1、什么是数据结构 2、为什么需要数据结构&#xff1f; 2、顺序表 1、顺序表的概念及结构 1.1 线性表 2、顺序表分类 3、动态顺序表的实现 总…

ssm+vue的高校疫情防控管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的高校疫情防控管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结…