头歌Java数据结构实训作业——常用算法图论,新手小白收藏这篇就够了
L4阶段:大模型的微调和私有化部署,我们会更加深入的探讨Transformer架构,学习大模型的微调技术,利用DeepSpeed、Lamam Factory等工具快速进行模型微调;而L3 L4更多的是通过项目实战来掌握大模型的应用开发,针对以上大模型的学习路线我们也整理了对应的学习视频教程,和配套的学习资料。但你不能只干Java,学习是学无止境的,更不要说这是个与时俱进、优胜劣汰的时代,技术发展的
一.Java查找算法之折半查找
相关知识:折半查找又叫作二分查找,是一种在有序数组中查找某一特定元素的搜索算法,使用二分可以极大地缩小我们搜索的时间复杂度
第1关:折半查找(二分查找)
1.任务描述
本关任务:给定一个排好序的数组,然后输入另一个整数,判断该整数在数组中的什么位置,返回该整数第一次出现的位置(位置从0开始),否则返回-1。
样例 1:
测试输入: n = 6 ,nums = [-1,0,3,5,9,12], T = 9 预期输出: 4 解释: 9 出现在 nums 中并且下标为 4
样例 2:
测试输入: n = 6,nums = [-1,0,3,5,9,12], T = 2 预期输出: -1 解释: 2 不存在 nums 中因此返回 -1
2.思路分析
最基础的二分查找,以后所有的二分都是在此基础上进行的改进
3.本关答案
package step1;
public class Task {
public int search(int n,int[] nums,int T){
/********* Begin *********/
int l=0;//左指针
int r=n-1;//右指针
//搜索区域: [0,n-1] 搜索结果: 等于T的索引
while(l<=r){
int mid=(l+r)/2;//区间中间比较值
if(nums[mid]>T){//要找的数在区间左边
r=mid-1;
}else if(nums[mid]<T){//要找的数在区间右边
l=mid+1;
}else{//找到了
return mid;
}
}
return -1;
/********* End *********/
}
}
第2关:求平方根
1.任务描述
本关任务:编写一个能求出整数x平方根的小程序。
使用二分查找,实现int mySqrt(int x)函数,求平方根的功能。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
2.思路分析
简而言之就是找到第一个平方<=target的元素索引
3.本关答案
package step2;
public class Task {
public int mySqrt(int x){
/********* Begin *********/
// 寻找第一个小于等于target的元素
int l=0;
int r=x;
int ans=-1;
//搜索区间: [1,x] 搜索结果: 第一个平方<=target的元素索引
while(l<=r){
int mid = (l+r)/2;
if((long) mid * mid <= x){
l=mid+1;
ans=mid;
}else{
r=mid-1;
}
}
return ans;
/********* End *********/
}
}
第3关:搜索数组
1.任务描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组[0,1,2,4,5,6,7]可能变为[4,5,6,7,0,1,2])。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它在数组中的下标,否则返回 -1 。
已知的是,数组中不存在重复的元素。
你的算法时间复杂度必须是 O(logn) 级别。
2.思路分析
二分查找的前提是有序!!!但这题是在原来有序的数组基础上进行了反转,最后我们可以看作是两个有序区间:右区间(大数)以及左区间(小数)
因此本题我们的思路就是在两个有序的区间中找到答案,因此我的重点就是如何在两个区间中来回寻找

首先我们要判断mid在哪个区间中,若是在右区间中,我们就在右区间进行区间缩小;若是在左区间中,我们就在左区间进行区间缩小。
当mid在右区间时,我们要考虑两种情况:什么条件才能区间左移或右移
这里当T也在右区间且小于T,向左缩小查找区间

这里无论T在不在右区间,我们都要右移

mid在左区间时同理
这样就是在两个有序区间中进行二分的逻辑
3.本关答案
package step3;
public class Task {
public int search(int n,int[] nums,int T){
/********* Begin *********/
if (n == 1) {
return nums[0] == T ? 0 : -1;
}
//搜索区间: [右半段] & [左半段] 搜索结果: 等于target的索引
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] == T) {//找到了
return mid;
}
if (nums[0] <= nums[mid]) {//mid在右半段区间中
if (nums[0] <= T && T < nums[mid]) {//T也在右区间,向左缩小查找区间
r = mid - 1;
} else {//向右缩小查找区间
l = mid + 1;
}
} else {//mid在右半段区间中
if (nums[mid] < T && T <= nums[n - 1]) {//T也在左区间,向右缩小查找区间
l = mid + 1;
} else {//向左缩小查找区间
r = mid - 1;
}
}
}
return -1;
/********* End *********/
}
}
二.Java 数据结构之排序
第1关:选择排序
1.任务描述
本关任务:实现选择排序。
- 补全
void sort(int arr[])方法,实现选择排序,对数组arr中的元素排序,并输出每一次排序后的结果。
2.思路分析
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕
其核心思想是在每一个索引位置,寻找其后面元素组的最小(大)元素交换

3.本关答案
package step1;
/**
* Created by sykus on 2018/3/20.
*/
public class SelectionSort {
/**
* 选择排序
*
* @param arr
*/
public static void sort(int arr[]) {
/********** Begin *********/
int n=arr.length;
for(int i=0;i<n-1;i++){//待排列元素
for(int j=i+1;j<n;j++){//未排列元素
if(arr[j]<arr[i]){
int tem=arr[i];
arr[i]=arr[j];
arr[j]=tem;
}
}
print(arr);
}
/********** End *********/
}
private static void print(int arr[]) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
第2关:插入排序
1.任务描述
本关任务:实现插入排序。
- 补全
sort(int arr[])方法,实现插入排序功能,并且输出每一次排序结果。
2.思路分析
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数组。
我们可以将待排序的数组分为两部分:有序数组和无序数组;那么插入排序就是将无序数组中的元素,插入到有序数组中

3.本关答案
package step2;
/**
* Created by sykus on 2018/3/20.
*/
public class InsertionSort {
public static void sort(int arr[]) {
/********** Begin *********/
for(int i=1;i<arr.length;i++){//遍历无序数组,对每一个元素进行排序
int j=i;
int tem=arr[j];
//与有序数组的元素比较,找到合适的位置
while(j>0 && tem<arr[j-1]){
arr[j]=arr[j-1];
j--;
}
arr[j]=tem;
print(arr);
}
/********** End *********/
}
private static void print(int arr[]) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
第3关:归并排序
1.任务描述
本关任务:实现归并排序。
- 补全
merge(int arr[], int p, int q, int r)方法,对数组arr中的两个序列arr[p..q]和arr[p+1..r]实现归并操作。
2.思路分析
归并是指将若干个已排序的子序列合并成一个有序的序列。
归并排序的核心就是先分组再排序再合并;我们先只看一路递归,对于未排序的数组,我们要将其分为两组子序列,再将这两组子序列继续递归排序成为有序的两组子序列,最后我们再将这两组子序列合并,就得到了一个排序好的数组了

3.本关答案
package step3;
/**
* Created by sykus on 2018/3/20.
*/
public class MergeSort {
/**
* lo, hi都是指下标
*/
public static void sort(int arr[], int lo, int hi) {
if (lo < hi) {
int mid = (lo + hi) / 2;//开始分组
//对两组子序列递归排序
sort(arr, lo, mid);
sort(arr, mid + 1, hi);
merge(arr, lo, mid, hi);//合并排序
print(arr);
}
}
private static void merge(int arr[], int p, int q, int r) {
/********** Begin *********/
//临时数组,用来记录合并后的数组
int[] tem=new int[arr.length];
//要合并的两个数组的索引下标
int i=p;
int j=q+1;
//记录临时数组tem的索引
int k=p;
//排序,填充临时数组
while(i<=q && j<=r){
if(arr[i]<arr[j]){
tem[k++]=arr[i++];
}else{
tem[k++]=arr[j++];
}
}
//加入剩余的数组
while(i<=q){
tem[k++]=arr[i++];
}
while(j<=r){
tem[k++]=arr[j++];
}
//将临时数组拷贝给arr
for(int a=p;a<=r;a++){
arr[a]=tem[a];
}
/********** End *********/
}
private static void print(int arr[]) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
第4关:快速排序
1.任务描述
本关任务:实现快速排序。
- 补全方法
sort(int arr[], int low, int high),实现快速排序功能。low,high为数组下标。 - 程序输出每次交换的结果。
2.思路分析
快速排序就是:通过一趟排序将要排序的数据分割成两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
因此对于每轮递归,我们要按其基准数排序将数组分为两个有序序列,再对这两个子序列进行递归重复操作

public void sort(int arr[], int low, int high) {
//记录查找的范围
int i=low;
int j=high;
//递归出口
if(low>high){
return;
}
//基准数
int baseNum=arr[low];
while(i!=j){
//找比基准数小的数
while(true){
if(j<=i || arr[j]<baseNum){
break;
}
j--;
}
//找比基准数大的数
while(true){
if(j<=i || arr[i]>baseNum){
break;
}
i++;
}
if(i>=j) break;
//交换数据,使比基准数小的在左边,大的在右边
int tem=arr[i];
arr[i]=arr[j];
arr[j]=tem;
}
//基准数归位
int tem=arr[i];
arr[i]=arr[low];
arr[low]=tem;
//递归进行下一轮基准数排序
sort(arr,low,i-1);
sort(arr,i+1,high);
}
3.本关答案
package step4;
/**
* Created by sykus on 2018/3/20.
*/
public class QuickSort {
public void sort(int arr[], int low, int high) {
/********** Begin *********/
int i=low;
int j=high+1;
int povit=arr[low];
while(i<j){
while(j>low && arr[--j]>=povit);
while(i<high && arr[++i]<=povit);
if(i>=j) break;
int tem=arr[j];
arr[j]=arr[i];
arr[i]=tem;
print(arr);
}
int tem=arr[j];
arr[j]=arr[low];
arr[low]=tem;
print(arr);
if(i>low) sort(arr,low,j-1);
if(j<high)sort(arr,j+1,high);
/********** End *********/
}
private static void print(int arr[]) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
第5关:堆排序
1.任务描述
本关任务:在本关,我们将实现另一种排序算法——堆排序(heapsort)。
-
平台将调用
HeapSort类的sort(int arr[])方法,对arr进行排序,并输出每次堆排序的结果 -
接着根据程序的输出判断程序是否正确。
2.思路分析
堆排序(`Heapsort`)是指利用堆这种数据结构所设计的一种排序算法,其中堆分为大顶堆和小顶堆。
堆的定义:当一颗二叉树的每个节点都大于等于它的两个子字节时,它被称为堆有序。而堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级储存
堆的性质:在索引起始位置为0的情形中: 下标为i的节点的父节点下标:(i-1)/2;下标为i的节点的左孩子下标:i\*2+1;下标为i的节点的右孩子下标:i\*2+2

堆排序(大顶堆):首先我们要先建堆,将未排序的数组进行建堆处理使其成为一个大顶堆
弗洛伊德建堆算法思想:找到最后一个非叶子节点,从后向前依次下潜(维护该节点树的堆的性质),下潜就是该节点与其左右两个孩子的最大者进行交换,重复此操作,直到叶子结点。
当我们建堆完成后就得到了一个大顶堆,而此时arr\[0\]即为堆顶元素
然后我们再来进行堆排序,其思想就是将堆顶元素与数组末尾元素交换,然后,去除交换后的排序数组,我们再去维护交换后的堆使其再次成为一个大顶堆,重复操作

3.本关答案
package step5;
/**
* Created by sykus on 2018/3/20.
*/
public class HeapSort {
public static void sort(int arr[]) {
/********** Begin *********/
int n=arr.length;
//建堆(弗洛伊德建堆算法)
//找到最后一个非叶子节点(n/2-1),从后向前依次下潜,维护堆的性质
for(int i=n/2-1;i>=0;i--){
down(arr,i,n);
}
//堆排序
//堆顶元素后移,维护堆
for(int i=n-1;i>0;i--){
int tem=arr[0];
arr[0]=arr[i];
arr[i]=tem;
down(arr,0,i);
print(arr);
}
/********** End *********/
}
//下潜维护堆的性质
//parent:待维护节点的下标 n:维护的元素范围[0,n]
public static void down(int[] arr,int parent,int n){
//先假设父节点最大,比较得到三者的最大节点下标
int max=parent;
int leftchild=2*parent+1;
int rightchild=2*parent+2;
if(leftchild<n && arr[leftchild]>arr[max]){
max=leftchild;
}
if(rightchild<n && arr[rightchild]>arr[max]){
max=rightchild;
}
//若假设的父节点不是最大,则交换继续向下下潜维护堆
if(max!=parent){
int tem=arr[parent];
arr[parent]=arr[max];
arr[max]=tem;
parent=max;
down(arr,parent,n);
}
}
private static void print(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
三.Java 数据结构之图
第1关:图的表示
1.任务描述
本关任务:学习图的相关概念和表示,并用邻接表示图。
-
平台将创建用户补全后的
Graph类的对象。 -
调用对象的
addEdge(int v, int w)方法,添加边构造一个图。 -
接着根据程序的输出判断程序是否正确。
2.相关知识
图由顶点(`Vertex`)和边(`Edge`)组成,顶点的集合是`V`、边的集合是`E`的图记为`G=(V, E)`,连接两点`u`和`v`的边用`e=(u, v)`表示

图的种类:图大体上分为`2`种。边没有指向性的图叫做无向图,边具有指向性的图叫做有向图。边上带有权值的图叫带权图


图的表示:邻接矩阵:使用大小为`|V|×|V|`的二维数组`G`来表示图。`G[i][j]`表示的是顶点`i`和顶点`j`的关系;邻接表:用一点到另一点的边在链表中来表示图

3.思路分析
本关要求我们完成图的邻接矩阵表示,首先我们来分析代码中已经给出的成员变量以及方法
其中给出了3个成员变量:V,E和一个邻接表;一个有参构造:是对于成员变量的初始化
我们需要了解的是给出的邻接表表示的是:每个顶点与之有关系的点的集合的数组

private int V;//顶点数
private int E;//边数
private ArrayList<Integer>[] adj;//邻接表,集合数组
public Graph(int v) {
if (v < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative");
V = v;
E = 0;
//index+1=v 索引+1=顶点
//如:1顶点在0索引处
adj = new ArrayList[V + 1];
for (int i = 0; i <= this.V; i++) {
adj[i] = new ArrayList<Integer>();
}
}
本关的任务简言之就是写出一个添加两点之间的边的方法,那么我们就直接在邻接表中赋值即可
4.本关答案
package step1;
import java.util.ArrayList;
public class Graph {
private int V;//顶点数
private int E;//边数
private ArrayList<Integer>[] adj;//邻接表,集合数组
public Graph(int v) {
if (v < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative");
V = v;
E = 0;
adj = new ArrayList[V + 1];
for (int i = 0; i <= this.V; i++) {
adj[i] = new ArrayList<Integer>();
}
}
public void addEdge(int v, int w) {
/********** Begin *********/
adj[v].add(w);
adj[w].add(v);
E++;
/********** End *********/
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append(V + " 个顶点, " + E + " 条边\n");
for (int v = 1; v <= V; v++) {
s.append(v + ": ");
for (int w : adj[v]) {
s.append(w + " ");
}
s.append("\n");
}
return s.toString();
}
}
第2关:深度优先搜索
1.任务描述
本关任务:实现深度优先搜索。
-
平台将创建用户补全后的
DFSGraph类的对象; -
调用对象的
void addEdge(int v, int w)方法,构造一个图; -
调用对象的
void DFS(int v)方法,从顶点v开始进行深度优先搜索,测试文件中是从1号顶点开始; -
接着根据程序的输出判断程序是否正确
2.相关知识
图的深度优先搜索(`Depth-First Search,DFS`),顾名思义就是深度遍历节点,也就是依次向下遍历节点的关系节点,直到该节点没有下一个关系节点或该节点已经遍历过了,我们再返回遍历最先前节点的其余节点
如下图所示:先遍历的A,然后遍历A的关系节点C,递归,再遍历C的关系节点B,B无关系节点,回溯到C,再遍历C的关系节点D,D的关系节点A遍历过了,回溯到C,C遍历完成,回溯到A,A的关系节点D,D遍历过了跳过,再遍历A的关系节点F,一直向下递归遍历,直到所有节点都遍历完成

3.思路分析
深度优先搜索是一个递归的过程,我们需要依次向下遍历节点的关系节点,直到该节点没有下一个关系节点或该节点已经遍历过了,我们再返回遍历最先前节点的其余节点
因此我们需要一个boolean数组用来记录节点是否遍历过
4.本关答案
package step2;
import java.util.ArrayList;
public class DFSGraph {
private boolean[] marked;
private int V;//顶点数
private int E;//边数
private ArrayList<Integer>[] adj;//邻接表
public DFSGraph(int v) {
if (v < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative");
V = v;
E = 0;
adj = new ArrayList[V + 1];
marked = new boolean[V + 1];
for (int i = 0; i <= this.V; i++) {
adj[i] = new ArrayList<Integer>();
}
}
public void addEdge(int v, int w) {
adj[v].add(w);
adj[w].add(v);
E++;
}
public void DFS(int v) {
/********** Begin *********/
if(marked[v]){//该节点遍历过了,回溯
return;
}
//遍历该节点
marked[v] = true;
System.out.print(v + " ");
//遍历与该节点有关系且未遍历的节点
for (int w : adj[v]) {
if (!marked[w]) {
DFS(w);//递归
}
}
/********** End *********/
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append(V + " 个顶点, " + E + " 条边\n");
for (int v = 1; v <= V; v++) {
s.append(v + ": ");
for (int w : adj[v]) {
s.append(w + " ");
}
s.append("\n");
}
return s.toString();
}
}
第3关:广度优先搜索
1.任务描述
本关任务:实现图的广度优先搜索
-
平台将创建用户补全后的
BFSGraph类的对象; -
调用对象的
void addEdge(int v, int w)方法,构建一个图; -
调用对象的
void BFS(int s)方法从结点s开始广度优先搜素; -
接着根据程序的输出判断程序是否正确。
2.相关知识
广度优先搜索算法(`Breadth First Search`),又称为"宽度优先搜索"或"横向优先搜索",简称`BFS,也就是先遍历一个节点的所有节点,再向下遍历它的关系节点中未遍历的节点`

3.思路分析
我们要先遍历该节点的所有关系节点,再去遍历其它的未遍历节点,那么我们就要在遍历该节点的时候用一个队列记录它的关系节点,队列先进先出的性质,这样我们就可以完成先遍历关系节点,再去遍历关系节点的关系节点的广度优先了
4.本关答案
package step3;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
public class BFSGraph {
private int V;//顶点数
private int E;//边数
private boolean[] marked;
private ArrayList<Integer>[] adj;//邻接表
public BFSGraph(int v) {
if (v < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative");
V = v;
E = 0;
adj = new ArrayList[V + 1];
marked = new boolean[V + 1];
for (int i = 0; i <= this.V; i++) {
adj[i] = new ArrayList<Integer>();
}
}
public void addEdge(int v, int w) {
adj[v].add(w);
adj[w].add(v);
E++;
}
public void BFS(int s) {
/********** Begin *********/
//从s节点开始遍历
Queue<Integer> que = new LinkedList<>();
que.offer(s);
marked[s] = true;
while (!que.isEmpty()) {
//遍历该节点
int v = que.poll();
System.out.print(v + " ");
//将该节点的未遍历的关系节点入队
for (int w : adj[v]) {
if (!marked[w]) {
que.offer(w);
marked[w] = true;
}
}
}
/********** End *********/
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append(V + " 个顶点, " + E + " 条边\n");
for (int v = 1; v <= V; v++) {
s.append(v + ": ");
for (int w : adj[v]) {
s.append(w + " ");
}
s.append("\n");
}
return s.toString();
}
}
第4关:单源最短路径
1.任务描述
本关任务:实现Dijkstra算法求单源最短路径
-
平台将创建用户补全后的
ShortestPath类的对象; -
调用对象的
addEdge(int u, int v, int w)方法,添加边和边的权重信息,构建图G; -
调用对象的
Paths(int source)方法执行Dijkstra算法,求最短路径,并输出返回的最短路径,这里源点设置为1; -
接着根据程序的输出判断程序是否正确
2.相关知识
单源最短路径:从某一个点开始,到其他所有点的最短路径
迪杰斯特拉算法(Dijkstra算法):是一个按路径长度递增的次序产生最短路径的算法,要求所有边的权重都为**非负值**
运用贪心的思想,局部:对于每一次的路径,我们首先要选择未走过路径的最小权值路径min,对于min的每一个关系节点,若min的权值到该节点的权值小于该节点的先前节点,就将更换该节点的权值;由局部推出整体为最优
如下图所演示:由A到其他所有点的最短路径
首先我们要先将路径初始化,源点到源点为0,其余先设为无穷大

然后选取权值最小点A,更新由它到关系节点的最小权值,更新完成后去除该点

再选取最小权值点D,并更新去除

接着重复操作


最后遍历完成后所取得的各点权值即为源点到各点的最短路径
3.思路分析
我们根据迪杰斯特拉算法的思想,可以获取到以下的步骤:首先获取最小权值点,再更新权值,再删除节点,重复操作直到所有节点都走过
获取最小权值点,我们可以利用集合实现,每次遍历集合比较获取;也可以利用优先队列实现,每次获取堆顶元素
4.本关答案
package step4;
import java.util.*;
public class ShortestPath {
private int V;//顶点数
private int E;//边数
private int[] dist;
private ArrayList<Integer>[] adj;//邻接表
private int[][] weight;//权重
public ShortestPath(int v, int e) {
V = v;
E = e;
dist = new int[V + 1];
adj = new ArrayList[V + 1];
weight = new int[V + 1][V + 1];
for (int i = 0; i <= this.V; i++) {
adj[i] = new ArrayList<Integer>();
}
}
public void addEdge(int u, int v, int w) {
adj[u].add(v);
adj[v].add(u);
weight[u][v] = weight[v][u] = w;
}
public int[] Paths(int source) {
/********** Begin *********/
ArrayList<Integer> list = new ArrayList<>();
boolean[] flag=new boolean[V+1];
// 源点到源点的距离为0
dist[source] = 0;
// 初始化
for (int i = 1; i <= V; i++) {
// 从源点到各个节点的距离初始化为无穷大
if (i != source) {
dist[i] = Integer.MAX_VALUE;
}
//index+1=v
list.add(i);
}
while (!list.isEmpty()) {
//获取权值最小点
int min=list.get(0);
for(int i=0;i<list.size();i++){
min=dist[min]<dist[list.get(i)]?min:list.get(i);
}
flag[min]=true;
//更新权值
for (int e : adj[min]) {
if(!flag[e]){
int alt = dist[min] + weight[min][e];
// 找到了到u的更短的路径
if (alt < dist[e]) {
dist[e] = alt;
}
}
}
//删除节点
list.remove((Integer)min);
}
return dist;
/********** End *********/
}
/**
* 打印源点到所有顶点的距离,INF为无穷大
*
* @param dist
*/
public void print(int[] dist) {
for (int i = 1; i <= V; i++) {
if (dist[i] == Integer.MAX_VALUE) {
System.out.print("INF ");
} else {
System.out.print(dist[i] + " ");
}
}
}
}
Java还能继续干下去吗
答案是“当然能”
但你不能只干Java,学习是学无止境的,更不要说这是个与时俱进、优胜劣汰的时代,技术发展的太快,如果还保持这传统思想与学习习惯,那你一定会被淘汰。
这里给大家提供介绍一条道路,将Java与大模型结合起来。
自2025年初,deepseek的横空出世,带火了国内的大模型发展,也让美国感受到了危机,大模型的热点持续不下,未来发展也势不可挡,将其与Java结合又具有很大优势:
一、Java与大模型结合的技术优势
-
推理环节的核心地位
大模型训练依赖Python生态的高性能计算资源,而Java在推理阶段(模型部署、性能优化、系统集成)具有独特优势。其“编写一次,处处运行”的特性,使其能无缝集成到微服务、分布式系统等企业级架构中,高效处理高并发请求。例如,某电商平台通过Java构建的大模型API网关,支撑每日千万级请求的稳定运行,响应时间缩短50%。 -
生态成熟与性能稳定
Java拥有Spring Boot、Spring Cloud等成熟框架,可快速实现服务注册、负载均衡、熔断降级等生产级能力。JVM的垃圾回收机制和即时编译技术,使其在长连接、高并发场景下表现优于脚本语言。例如,某金融系统采用Java实现大模型推理服务,系统可用率长期保持99.99%以上。 -
兼容性与工程化能力
Java与现有业务系统的兼容性极强,可降低大模型落地的集成成本。例如,某制造企业通过Java将大模型与ERP系统对接,实现生产流程的智能优化,故障率降低30%。同时,Java在代码规范、测试流程、版本管理等方面的积累,能大幅降低大模型项目的研发成本和维护难度。
二、市场需求与薪资水平
-
高薪岗位涌现
大厂对“Java+大模型”复合人才的开价普遍达到传统Java开发岗位的3倍。例如,某互联网企业招聘大模型推理工程师,要求精通Java与大模型框架,薪资范围为40K-70K·15薪,远高于传统Java开发的15K-30K·13薪。 -
岗位需求激增
随着大模型技术的普及,IT行业催生出大量“高需求、高薪资”的新岗位,如AI工程师、大模型推理工程师、智能应用开发工程师等。这些岗位的核心要求是“Java基础+大模型能力”,例如:-
AI工程师:需掌握Java、深度学习框架(如PyTorch/TensorFlow的Java API)、工程化工具(如Docker、Kubernetes)。
-
大模型推理工程师:需熟悉Java性能优化、高并发设计、大模型API调用(如OpenAI、文心一言的Java适配器)。
-
因此捕获AI,掌握技术是关键,让AI成为我们最便利的工具.
一定要把现有的技术和大模型结合起来,而不是抛弃你们现有技术!掌握AI能力的Java工程师比纯Java岗要吃香的多。
即使现在裁员、降薪、团队解散的比比皆是……但后续的趋势一定是AI应用落地!大模型方向才是实现职业升级、提升薪资待遇的绝佳机遇!
如何学习AGI大模型?
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
2025最新版CSDN大礼包:《AGI大模型学习资源包》免费分享**
一、2025最新大模型学习路线
一个明确的学习路线可以帮助新人了解从哪里开始,按照什么顺序学习,以及需要掌握哪些知识点。大模型领域涉及的知识点非常广泛,没有明确的学习路线可能会导致新人感到迷茫,不知道应该专注于哪些内容。
我们把学习路线分成L1到L4四个阶段,一步步带你从入门到进阶,从理论到实战。

L1级别:AI大模型时代的华丽登场
L1阶段:我们会去了解大模型的基础知识,以及大模型在各个行业的应用和分析;学习理解大模型的核心原理,关键技术,以及大模型应用场景;通过理论原理结合多个项目实战,从提示工程基础到提示工程进阶,掌握Prompt提示工程。

L2级别:AI大模型RAG应用开发工程
L2阶段是我们的AI大模型RAG应用开发工程,我们会去学习RAG检索增强生成:包括Naive RAG、Advanced-RAG以及RAG性能评估,还有GraphRAG在内的多个RAG热门项目的分析。

L3级别:大模型Agent应用架构进阶实践
L3阶段:大模型Agent应用架构进阶实现,我们会去学习LangChain、 LIamaIndex框架,也会学习到AutoGPT、 MetaGPT等多Agent系统,打造我们自己的Agent智能体;同时还可以学习到包括Coze、Dify在内的可视化工具的使用。

L4级别:大模型微调与私有化部署
L4阶段:大模型的微调和私有化部署,我们会更加深入的探讨Transformer架构,学习大模型的微调技术,利用DeepSpeed、Lamam Factory等工具快速进行模型微调;并通过Ollama、vLLM等推理部署框架,实现模型的快速部署。

整个大模型学习路线L1主要是对大模型的理论基础、生态以及提示词他的一个学习掌握;而L3 L4更多的是通过项目实战来掌握大模型的应用开发,针对以上大模型的学习路线我们也整理了对应的学习视频教程,和配套的学习资料。
二、大模型经典PDF书籍
书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础。(书籍含电子版PDF)

三、大模型视频教程
对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识。

四、大模型项目实战
学以致用 ,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。

五、大模型面试题
面试不仅是技术的较量,更需要充分的准备。
在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
更多推荐


所有评论(0)