怒刷LeetCode的第2天(Java版)

目录

第一题

题目来源

题目内容

解决方法

方法一:滑动窗口

方法二:双指针加哈希表

第二题

题目来源

题目内容

解决方法

方法一:二分查找

方法二:归并排序

方法三:分治法

第三题

题目来源

题目内容

解决方法

方法一:动态规划

方法二:中心扩展法

方法三:Manacher 算法


第一题

题目来源

3. 无重复字符的最长子串 - 力扣(LeetCode)

题目内容

解决方法

方法一:滑动窗口

该问题可以使用滑动窗口算法来解决。滑动窗口是一种通过移动窗口的起始和结束位置来解决字符串/数组子串问题的常用技巧。

具体算法步骤如下:

  1. 定义一个哈希集合,用于存储当前窗口中的字符。
  2. 使用两个指针left和right分别表示窗口的起始和结束位置,初始化为0。
  3. 使用一个循环遍历字符串s,不断移动右指针来扩展窗口。当右指针指向的字符在哈希集合中存在时,说明窗口中出现了重复字符。
  4. 在每一次循环中,首先判断右指针指向的字符是否在哈希集合中存在,如果不存在,则将该字符加入集合,并更新最长无重复子串的长度为maxLen(即maxLen = max(maxLen, right - left + 1))。
  5. 如果右指针指向的字符在哈希集合中存在,则从窗口的左侧开始移动左指针,直到窗口中不再有重复字符。
  6. 重复步骤3-5,直到右指针到达字符串s的末尾。
  7. 返回最长无重复子串的长度maxLen。
class Solution {public int lengthOfLongestSubstring(String s) {int n = s.length();Set<Character> set = new HashSet<>();int maxLen = 0, left = 0, right = 0;while (right < n) {if (!set.contains(s.charAt(right))) {set.add(s.charAt(right));maxLen = Math.max(maxLen, right - left + 1);right++;} else {set.remove(s.charAt(left));left++;}}return maxLen;
}
}

该算法的时间复杂度为O(n),其中n是字符串s的长度。在最坏情况下,每个字符都需要遍历一次。空间复杂度为O(min(n, m)),其中m是字符集的大小。在最坏情况下,窗口中可能包含所有的字符。 

LeetCode运行结果:

方法二:双指针加哈希表

除了滑动窗口算法之外,还可以使用双指针加哈希表来解决该问题。

具体算法步骤如下:

  1. 定义一个哈希表map,用于存储字符和它在字符串中出现的位置。
  2. 使用两个指针left和right分别表示当前无重复子串的起始和结束位置,初始化为0。
  3. 使用一个循环遍历字符串s,不断移动右指针来扩展窗口。当右指针指向的字符在哈希表中存在时,说明窗口中出现了重复字符。
  4. 在每一次循环中,首先判断右指针指向的字符是否在哈希表中存在,如果不存在,则将该字符加入哈希表,并更新最长无重复子串的长度为maxLen(即maxLen = max(maxLen, right - left + 1))。
  5. 如果右指针指向的字符在哈希表中存在,则从哈希表中获取该字符最新的出现位置pos,并将左指针移动到pos+1的位置。
  6. 更新哈希表中该字符的最新出现位置为右指针指向的位置right+1(注意要加1,因为要排除当前字符的影响),然后将右指针继续右移一位。
  7. 重复步骤3-6,直到右指针到达字符串s的末尾。
  8. 返回最长无重复子串的长度maxLen。
class Solution {
public int lengthOfLongestSubstring(String s) {int n = s.length();Map<Character, Integer> map = new HashMap<>();int maxLen = 0, left = 0, right = 0;while (right < n) {char c = s.charAt(right);if (map.containsKey(c)) {left = Math.max(left, map.get(c) + 1);}map.put(c, right);maxLen = Math.max(maxLen, right - left + 1);right++;}return maxLen;
}
}

该算法的时间复杂度为O(n),其中n是字符串s的长度。在最坏情况下,每个字符都需要遍历一次。空间复杂度为O(min(n, m)),其中m是字符集的大小。在最坏情况下,哈希表中可能包含所有的字符。

LeetCode运行结果:

第二题

题目来源

4. 寻找两个正序数组的中位数 - 力扣(LeetCode)

题目内容

解决方法

方法一:二分查找

本题可以使用二分查找求解,时间复杂度为O(log(min(m, n)))。

由于两个数组都是有序的,所以可以先将问题转化为寻找第k小的数,其中k等于两个数组的长度之和除以2。如果两个数组长度之和是奇数,则中位数就是第k小的数;如果长度之和是偶数,则中位数是第k小和第k+1小数的平均值。

具体算法如下:

  1. 设两个有序数组为nums1和nums2,分别对它们进行二分查找。
  2. 假设nums1的长度为m,nums2的长度为n,则第k小的数就是最小的第k/2个数。
  3. 比较nums1[k/2-1]和nums2[k/2-1]。如果nums1[k/2-1] < nums2[k/2-1],说明nums1中的前k/2个数一定包含在中位数的前k个数中。反之,如果nums1[k/2-1] > nums2[k/2-1],说明nums2中的前k/2个数一定包含在中位数的前k个数中。
  4. 根据步骤3中的比较结果,确定接下来需要在哪个子数组中查找,然后递归调用本函数,更新k和目标数组。特别注意,每次递归调用时,都需要更新数组的起始下标,使得两个子数组的长度之和为k。
  5. 重复步骤3-4,直到k为1或者有一个数组为空,则中位数即为两个数组中剩余元素的第一个。
class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int m = nums1.length, n = nums2.length;if (m > n) {return findMedianSortedArrays(nums2, nums1);}int k = (m + n + 1) / 2;int left = 0, right = m;while (left < right) {int i = left + (right - left) / 2;int j = k - i;if (nums1[i] < nums2[j - 1]) {left = i + 1;} else {right = i;}}int i = left, j = k - i;int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i];int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];if ((m + n) % 2 == 0) {return (Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin)) / 2.0;} else {return Math.max(nums1LeftMax, nums2LeftMax);}
}}

该算法的时间复杂度为O(log(min(m, n))),空间复杂度为O(1)。 

LeetCode运行结果:

方法二:归并排序

还有另一种方法可以解决这个问题,即使用归并排序的思想。具体步骤如下:

  1. 创建一个新的数组,用于存储合并后的两个有序数组。
  2. 使用两个指针分别指向nums1和nums2的起始位置。
  3. 每次比较两个数组当前指针所指的元素,将较小的元素添加到新数组中,并将对应的指针向后移动一位。
  4. 重复步骤3,直到其中一个数组的指针到达末尾。
  5. 将另一个数组剩余的元素依次添加到新数组的末尾。
  6. 如果新数组的长度是奇数,则中位数就是新数组的中间元素;如果新数组的长度是偶数,则中位数是中间两个元素的平均值。

该方法的时间复杂度为O(m + n),其中m和n分别是两个数组的长度。空间复杂度为O(m + n),主要用于存储合并后的数组。 

class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int m = nums1.length, n = nums2.length;int[] merged = new int[m + n];int i = 0, j = 0, k = 0;while (i < m && j < n) {if (nums1[i] <= nums2[j]) {merged[k++] = nums1[i++];} else {merged[k++] = nums2[j++];}}while (i < m) {merged[k++] = nums1[i++];}while (j < n) {merged[k++] = nums2[j++];}if ((m + n) % 2 == 0) {int mid = (m + n) / 2;return (merged[mid - 1] + merged[mid]) / 2.0;} else {int mid = (m + n) / 2;return merged[mid];}
}}

LeetCode运行结果:

方法三:分治法

除了上述两种方法,还可以使用分治法来解决这个问题。该方法的思路是将问题分解为两个子问题,然后对子问题进行递归求解。

具体步骤如下:

  1. 分别找到两个数组的中位数,假设分别为nums1[mid1]和nums2[mid2],其中mid1和mid2分别表示两个数组的中间位置。
  2. 如果nums1[mid1]等于nums2[mid2],则说明中位数已经找到,直接返回nums1[mid1]或nums2[mid2]。
  3. 如果nums1[mid1]小于nums2[mid2],则说明中位数应该在nums1[mid1]的右侧和nums2[mid2]的左侧。
    • 在nums1[mid1]的右侧舍弃掉前mid1+1个元素,并且将k减去mid1+1。
    • 在nums2[mid2]的左侧舍弃掉后mid2个元素。
  4. 如果nums1[mid1]大于nums2[mid2],则说明中位数应该在nums1[mid1]的左侧和nums2[mid2]的右侧。
    1. 在nums1[mid1]的左侧舍弃掉后mid1个元素。
    2. 在nums2[mid2]的右侧舍弃掉前mid2+1个元素,并且将k减去mid2+1。
  5. 重复上述步骤3和步骤4,直到找到中位数为止。

这种方法的时间复杂度也为O(log(min(m, n))),空间复杂度为O(1)。与二分查找类似,它通过逐渐缩小问题规模来快速找到中位数。

class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int m = nums1.length, n = nums2.length;int total = m + n;if (total % 2 == 1) { // 奇数长度,中位数是第 total/2 + 1 个元素return getKthElement(nums1, nums2, total / 2 + 1);} else { // 偶数长度,中位数是第 total/2 个元素和第 total/2 + 1 个元素的平均值double left = getKthElement(nums1, nums2, total / 2);double right = getKthElement(nums1, nums2, total / 2 + 1);return (left + right) / 2.0;}
}private int getKthElement(int[] nums1, int[] nums2, int k) {int m = nums1.length, n = nums2.length;int index1 = 0, index2 = 0;while (true) {// 边界情况:一个数组的所有元素都被剔除if (index1 == m) {return nums2[index2 + k - 1];}if (index2 == n) {return nums1[index1 + k - 1];}// 边界情况:k=1,即找到了最小的一个数if (k == 1) {return Math.min(nums1[index1], nums2[index2]);}// 正常情况int newIndex1 = Math.min(index1 + k / 2, m) - 1;int newIndex2 = Math.min(index2 + k / 2, n) - 1;int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];if (pivot1 <= pivot2) {k -= newIndex1 - index1 + 1;index1 = newIndex1 + 1;} else {k -= newIndex2 - index2 + 1;index2 = newIndex2 + 1;}}
}}

LeetCode运行结果:

第三题

题目来源

5. 最长回文子串 - 力扣(LeetCode)

题目内容

解决方法

方法一:动态规划

这道题可以使用动态规划来解决,具体步骤如下:

  1. 定义一个二维布尔数组dp,其中dp[i][j]表示字符串s中从索引i到索引j的子串是否是回文子串。初始化时,将所有的dp[i][i]设为true,表示单个字符都是回文串。
  2. 从右下角开始,按列从下往上逐列填表。先计算长度为2的子串是否是回文子串,再计算长度为3的子串,依次类推,直到计算出整个字符串的最长回文子串长度为止。
    如果dp[i+1][j-1]为true(即子串s[i+1:j-1]也是回文子串)且s[i]等于s[j],则dp[i][j]为true,否则为false。 
  3. 在计算的过程中,记录并更新最长回文子串的起始索引和长度。
  4. 最后,根据最长回文子串的起始索引和长度,提取出最长回文子串。
class Solution {public String longestPalindrome(String s) {int n = s.length();boolean[][] dp = new boolean[n][n];int maxLen = 0;int start = 0;for (int j = 0; j < n; j++) {for (int i = j; i >= 0; i--) {if (s.charAt(i) == s.charAt(j) && (j - i <= 2 || dp[i + 1][j - 1])) {dp[i][j] = true;if (j - i + 1 > maxLen) {maxLen = j - i + 1;start = i;}}}}return s.substring(start, start + maxLen);
}}

该算法的时间复杂度为O(n^2),空间复杂度为O(n^2),其中n为字符串的长度。通过动态规划,我们可以高效地找到最长回文子串。 

LeetCode运行结果:

  

方法二:中心扩展法

除了动态规划方法外,还可以使用中心扩展法来解决这个问题。

中心扩展法的思路是,对于每个字符或每对相邻字符,以它们为中心向两边扩展,判断是否是回文串。具体步骤如下:

  1. 遍历字符串中的每个字符,将每个字符作为中心(回文串长度为奇数的情况)或相邻字符的中间位置(回文串长度为偶数的情况)。
  2. 在每个中心位置,使用双指针,从中心向两边扩展,判断左右的字符是否相等,直到无法再扩展为止。
  3. 在扩展过程中,记录并更新最长回文子串的起始索引和长度。
  4. 最后,根据最长回文子串的起始索引和长度,提取出最长回文子串。
class Solution {public String longestPalindrome(String s) {int n = s.length();int start = 0, maxLen = 0;for (int i = 0; i < n; i++) {// 中心为一个字符的情况int len1 = expandAroundCenter(s, i, i);// 中心为相邻字符的情况int len2 = expandAroundCenter(s, i, i + 1);int len = Math.max(len1, len2);if (len > maxLen) {maxLen = len;// 根据中心和回文串长度计算起始索引start = i - (len - 1) / 2;}}return s.substring(start, start + maxLen);
}private int expandAroundCenter(String s, int left, int right) {while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {left--;right++;}// 扩展的长度为 right-left-1,减1是因为不满足条件时left和right多移动了一步return right - left - 1;
}
}

该算法的时间复杂度为O(n^2),空间复杂度为O(1),其中n为字符串的长度。中心扩展法利用了回文串的特点,可以高效地找到最长回文子串。 

LeetCode运行结果:

方法三:Manacher 算法

除了动态规划和中心扩展法之外,还有一种称为Manacher算法的线性时间算法可以用于查找最长回文子串。Manacher算法的核心思想是利用回文串的对称性,在遍历过程中尽量复用已经计算过的回文子串信息。

具体步骤如下:

  1. 首先,我们需要对原始字符串进行预处理,将其转换为一个新的字符串,这样可以将奇数长度和偶数长度的回文串统一处理。
    • 在每个字符前后添加特殊字符(例如#),以确保新字符串中的回文串长度都是奇数。
    • 在新字符串的开头和结尾添加特殊字符(例如$%),以便处理边界情况。
    • 新字符串的长度将是原始字符串长度的两倍加一。
  2. 定义一个辅助数组P,其中P[i]表示以新字符串中索引i为中心的回文串的半径长度(包括中心字符在内)。
    • 使用两个变量centerright来维护当前已经找到的最右边界的回文串的中心和右边界。
    • 在遍历的过程中,逐个计算P[i]的值。
      • i在当前最右边界right的左侧时,使用对称性快速计算出初始猜测值,即P[i] = P[2 * center - i]。但如果该猜测值超出了最右边界,则需要修正为最右边界到边界之间的距离,即P[i] = right - i
      • 然后,利用中心扩展法向两边扩展,直到无法再扩展为止,更新P[i]的值并更新最右边界。
  3. 在遍历过程中,记录并更新最长回文子串的起始索引和长度。最长回文子串的长度即为maxLen = max(P) - 1,起始索引为start = (maxP - 1) / 2
  4. 最后,根据最长回文子串的起始索引和长度,提取出最长回文子串。
class Solution {public String longestPalindrome(String s) {int n = s.length();StringBuilder sb = new StringBuilder("$#");// 预处理字符串for (int i = 0; i < n; i++) {sb.append(s.charAt(i));sb.append("#");}sb.append("%");String str = sb.toString();int len = str.length();int[] P = new int[len];int center = 0, right = 0;int maxP = 0, maxLen = 0;for (int i = 1; i < len - 1; i++) {if (i < right) {int mirror = 2 * center - i;P[i] = Math.min(right - i, P[mirror]);}// 中心扩展while (str.charAt(i + P[i] + 1) == str.charAt(i - P[i] - 1)) {P[i]++;}// 更新最右边界if (i + P[i] > right) {center = i;right = i + P[i];}// 记录最长回文子串的起始索引和长度if (P[i] > maxLen) {maxLen = P[i];maxP = i;}}int start = (maxP - maxLen) / 2;return s.substring(start, start + maxLen);
}
}

Manacher算法的时间复杂度为O(n),空间复杂度为O(n),其中n为字符串的长度。相比于动态规划和中心扩展法,Manacher算法在效率上具有优势,特别适用于处理大规模字符串。

LeetCode运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring AOP使用指南: 强大的面向切面编程技术

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

照片批量处理 7000张

需求&#xff1a; 有6700照片导入系统&#xff1b; 系统只支持500张/每次&#xff1b; 6700 按机构分类复制提取出来&#xff1b; 分批次导入&#xff1b; 6700 分17份复制到对应文件夹中&#xff1b; 照片按照学号命名的&#xff1b; 20231715401.jpg 开始用bat脚本…

9月8日作业

思维导图 栈stack.h #ifndef STACK_H #define STACK_H #include <iostream> #define MAXSIZE 128using namespace std; class Stack { public://构造函数Stack();//析构函数~Stack();//拷贝构造函数Stack(const Stack &other);//入栈bool push(int value);//出栈并返…

技师学院物联网实训室建建设方案

一、概述 1.1专业背景 物联网&#xff08;Internet of Things&#xff09;被称为继计算机、互联网之后世界信息产业第三次浪潮&#xff0c;它并非一个全新的技术领域&#xff0c;而是现代信息技术发展到一定阶段后出现的一种聚合性应用与技术提升&#xff0c;是随着传感网、通…

Docker 恶意挖矿镜像应急实例

01、概述 当网络流量监控发现某台运行多个docker容器的主机主动连接到一个疑似挖矿矿池的地址时&#xff0c;需要快速响应和排查&#xff0c;以阻止进一步的损害。 面对docker容器的场景下&#xff0c;如何快速分析和识别恶意挖矿容器?本文将分享一种应急响应思路&#xff0c;…

基于matlab实现的电力系统稳定性分析摆幅曲线代码

完整程序&#xff1a; clear; clc; t 0; tf 0; tfl 0.5; tc 0.5; % tc 0.05, 0.125, 0.5 sec for 2.5 cycles, 6.25 cycles & 25 cycles resp ts 0.05; m 2.52 / (180 * 50); i 2; dt 21.64 * pi / 180; ddt 0; time(1) 0; ang(1) 21.64; pm 0.9; pm1 2.44;…

Redis3.2.1如何设置远程连接?允许局域网访问

背景&#xff1a; 电脑A的redis需要开放给电脑B使用&#xff0c;二者处于同一局域网 【后面会补充更详细的踩坑历程&#xff0c;先发出来作为记录】 过程&#xff1a; 在你查了很多方法后&#xff0c;如果还是没有解决&#xff0c; 尝试考虑一下你的redis配置文件是不是修…

零基础VB6无壳P-CODE逆向分析(VB Decompiler应用与避坑)

> 前言 最近从朋友那里拿到了一个加密狗授权的软件安装包,秉承着LCG的精神,开启了逆向之路,经过查壳和综合分析确定是VB6编写的程序(这年头使用VB6开发商业程序的还真少见),作为一名C# Winform的业余程序员,靠着C#的知识勉强分析个大概. > 授权简介 软件共分三种授权模…

一场深刻的开源聚会:KCC@北京 9.2 活动回顾

开源为我们带来了什么&#xff1f;这是这场聚会的宣传文的标题&#xff1a;https://mp.weixin.qq.com/s/5sR6TPEpQmYNBnCtVilkzg 同样这个问题也可以是极具个体化的&#xff1a;开源为我带来了什么&#xff1f;秋天的周末&#xff0c;预报有雨&#xff0c;北京的开源人还是相聚…

脚本:用python实现五子棋

文章目录 1. 语言2. 效果3. 脚本4. 解读5. FutureReference 1. 语言 Python 无环境配置、无库安装。 2. 效果 以第一回合为例 玩家X 玩家0 3. 脚本 class GomokuGame:def __init__(self, board_size15):self.board_size board_sizeself.board [[ for _ in range(board_…

对IP协议概念以及IP地址的概念进行简单整理

网络层重要协议 参考模型和协议栈IP协议IPv4数据报IP数据报格式IPv4地址特殊IP地址私有IP地址和公有IP地址子网划分 参考模型和协议栈 IP协议 IP协议定义了网络层数据传送的基本单元&#xff0c;也制定了一系列关于网络层的规则。 IPv4数据报 网络层的协议数据单元PDU 叫做分…

基于Java+SpringBoot+Vue+uniapp点餐小程序(包含协同过滤算法和会员系统,强烈推荐!)

校园点餐小程序 一、前言二、我的优势2.1 自己的网站2.2 自己的小程序&#xff08;小蔡coding&#xff09;2.3 有保障的售后2.4 福利 三、开发环境与技术3.1 MySQL数据库3.2 Vue前端技术3.3 Spring Boot框架3.4 微信小程序 四、功能设计4.1 系统功能结构设计4.2 主要功能描述 五…

【初阶数据结构】栈和队列——C语言(详解)

目录 一、栈 1.1栈的概念及结构 1.2栈的实现 1.2.1静态栈的实现 1.3动态栈的实现 1.3.1栈的创建 1.3.2栈的初始化 1.3.3栈的清空销毁 1.3.4栈的元素插入 1.3.5栈顶元素的删除 1.3.6返回栈顶数据 1.3.7求栈的大小 1.3.8判断栈是否为空 二、栈的实现完整代码 三、队…

Unity 动画系统

动画系统包含&#xff1a; 动画片段 Animation Clip&#xff0c;记录物体变化的信息&#xff0c;可以是角色的闪转腾挪&#xff0c;也可以是一扇门的开闭动画状态机 Animator Controller&#xff0c;根据设置切换动画片段动画组件 Animator&#xff0c;Animation替身 Avatar&a…

数据结构基础8:二叉树oj+层序遍历。

二叉树oj层序遍历 题目一&#xff1a;二叉树的销毁&#xff1a;方法一&#xff1a;前序遍历&#xff1a;方法二&#xff1a;后序遍历&#xff1a; 题目二&#xff1a;二叉树查找值为x的节点方法一&#xff1a;方法二&#xff1a;方法三&#xff1a; 题目三&#xff1a;层序遍历…

透视俄乌网络战之二:Conti勒索软件集团(下)

透视俄乌网络战之一&#xff1a;数据擦除软件 透视俄乌网络战之二&#xff1a;Conti勒索软件集团&#xff08;上&#xff09; Conti勒索软件集团&#xff08;下&#xff09; 1. 管理面板源代码2. Pony凭证窃取恶意软件3. TTPs4. Conti Locker v2源代码5. Conti团伙培训材料6. T…

【Linux从入门到精通】多线程 | 线程互斥(互斥锁)

上篇文章我们对线程 | 线程介绍&线程控制介绍后&#xff0c;本篇文章将会对多线程中的线程互斥与互斥锁的概念进行详解。同时结合实际例子解释了可重入与不被重入函数、临界资源与临界区和原子性的概念。希望本篇文章会对你有所帮助。 文章目录 引入 一、重入与临界 1、1 可…

多线程|多进程|高并发网络编程

一.多进程并发服务器 多进程并发服务器是一种经典的服务器架构&#xff0c;它通过创建多个子进程来处理客户端连接&#xff0c;从而实现并发处理多个客户端请求的能力。 概念&#xff1a; 服务器启动时&#xff0c;创建主进程&#xff0c;并绑定监听端口。当有客户端连接请求…

GitLab使用的最简便方式

GitLab介绍 GitLab是一个基于Git版本控制系统的开源平台&#xff0c;用于代码托管&#xff0c;持续集成&#xff0c;以及协作开发。它提供了一套完整的工具&#xff0c;以帮助开发团队协同工作、管理和部署代码。 往往在企业内部使用gitlab管理代码&#xff0c;记录一下将本地代…

VR航天航空巡展VR科技馆航天主题科普设备沉浸遨游太空

每当飞机飞过头顶&#xff0c;我们总是忍不住抬头去仰望。从嫦娥奔月的神话传说&#xff0c;到莱特兄弟实现了上天翱翔的梦想&#xff0c;人类一直在不断探索更辽阔的天空和浩瀚的宇宙。 航空科普 寻梦而行 普乐蛙VR航天航空巡展&#xff0c;正在湖南郴州如火如荼的进行中&…