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

45. 跳跃游戏 II

目录

题目链接:

题目:

解题思路:

代码:

总结:


题目链接:

45. 跳跃游戏 II - 力扣(LeetCode)

题目:

解题思路:

记录当前阶段一个步骤能走到最远的距离,这时候再++,并且在里面进行判断,若超过直接结束循环即可

代码:

class Solution {public int jump(int[] nums) {if(nums.length==1) return 0;int res=0;int cur=0;int pre=0;for(int i=0;i<nums.length;i++){cur=Math.max(cur,nums[i]+i);if(i==pre){res++;pre=cur;if(pre>=nums.length-1) break;}}return res;}
}

深入解析 “跳跃游戏” 贪心算法:从直觉到证明
引言:什么是跳跃游戏?
“跳跃游戏”(Jump Game)是一个经典的贪心算法问题,也是面试中的常客。它的描述非常简单:
给定一个非负整数数组 nums,你最初位于数组的第一个下标。数组中的每个元素 nums[i] 表示你在该位置可以跳跃的最大长度。你的目标是判断自己是否能够到达数组的最后一个下标。
例如,对于数组 [2,3,1,1,4]:
你从索引 0 开始,nums[0] = 2 表示你可以跳 1 步到索引 1,或者跳 2 步到索引 2。
如果你跳到索引 1,nums[1] = 3 表示你可以从这里跳 1, 2, 或 3 步,最远可以到达索引 4,也就是最后一个下标。
因此,对于这个数组,答案是 true。
而对于数组 [3,2,1,0,4]:
你从索引 0 开始,最远能到达索引 3。
但到达索引 3 后,nums[3] = 0,你无法再向前跳跃。
因此,你永远无法到达最后一个索引 4,答案是 false。
本文将深入解析一个基于贪心策略的高效算法,它仅用一次遍历即可解决问题,时间复杂度为 O (n),空间复杂度为 O (1)。
算法核心代码
我们先来看这个高效求解 “跳跃游戏” 的算法实现:
java
运行
class Solution {
    public boolean canJump(int[] nums) {
        int i = 0;
        int maxidx = 0;
        for (i = 0; i < nums.length; i++) {
            // 如果当前索引超过了能到达的最远索引,说明无法到达这里,直接返回 false
            if (i > maxidx) return false;
            
            // 更新能到达的最远索引
            maxidx = Math.max(maxidx, i + nums[i]);
        }
        
        // 如果成功遍历完整个数组,说明可以到达最后一个索引
        return true;
    }
}
这段代码虽然短小精悍,但其中蕴含着深刻的贪心思想。它通过维护一个 “能到达的最远索引” 变量,巧妙地判断了是否能够到达终点。
算法设计思想:贪心策略的完美应用
问题分析与直觉
这个问题的核心是判断是否存在一条从起点到终点的路径。最直观的想法是模拟所有可能的跳跃,但这会导致指数级的时间复杂度,对于大规模数组是不可行的。
贪心算法的精髓在于在每一步做出局部最优的选择,并期望由这些局部最优解最终导出全局最优解。那么,在 “跳跃游戏” 中,什么是 “局部最优选择” 呢?
当我们位于索引 i 时,我们可以选择跳 1 步、2 步,...,最多 nums[i] 步。从贪心的角度看,为了尽可能地到达更远的地方,我们应该选择跳到能让我们达到最远未来位置的那个点。
但我们不需要真的去模拟这个 “选择” 过程。我们可以用一个变量 maxidx 来记录在当前位置以及之前所有位置中,我们能够到达的最远索引。这个 maxidx 就是我们的 “局部最优解” 的累积。
核心变量定义
maxidx:这是算法的核心。它代表在遍历到当前位置 i 时,从起点出发,通过任意次跳跃,所能到达的最远索引。
i:循环变量,代表我们当前所在的索引。
算法逻辑推演
初始化:
我们从索引 0 开始,所以 i 从 0 开始。
最初,我们能到达的最远索引就是从起点 0 出发能跳到的位置,即 0 + nums[0]。因此,maxidx 初始化为 0(在循环开始后会被立即更新)。
遍历与更新:
我们遍历数组中的每一个索引 i。
在访问索引 i 时,我们首先检查一个关键条件:if (i > maxidx)。
这个条件的含义是:“我当前想要访问的索引 i,是否已经超出了我之前所有努力能到达的最远范围 maxidx?”
如果 i > maxidx 成立,这意味着我们根本无法到达索引 i。既然连 i 都到达不了,那么位于 i 之后的终点自然也无法到达。此时,我们可以立即得出结论,返回 false。
如果 i <= maxidx,说明我们可以到达当前位置 i。那么,我们就有机会从 i 出发,跳得更远。
因此,我们更新 maxidx 的值:maxidx = Math.max(maxidx, i + nums[i])。
这个更新操作的含义是:“比较一下,是我之前记录的最远到达距离 maxidx 更远,还是从当前位置 i 出发能到达的最远距离 i + nums[i] 更远?” 我们取其中的较大值,作为新的 “能到达的最远索引”。
终止条件:
成功情况:如果循环能够正常结束,意味着我们成功访问了数组的最后一个元素。这说明最后一个元素的索引 nums.length - 1 必然小于或等于 maxidx(否则循环会提前中断)。因此,我们可以到达终点,返回 true。
失败情况:在循环过程中,如果触发了 if (i > maxidx) 的条件,说明出现了无法逾越的 “鸿沟”,返回 false。
算法详细解析:一步步跟踪
让我们用一个成功的例子 nums = [2, 3, 1, 1, 4] 和一个失败的例子 nums = [3, 2, 1, 0, 4] 来跟踪算法的执行过程。
示例 1:nums = [2, 3, 1, 1, 4] (成功案例)
循环次数    i (当前索引)    nums[i]    i > maxidx (检查)    i + nums[i] (从当前位置能跳的最远)    maxidx (更新后)    备注
初始状态    -    -    -    -    0    maxidx 初始为 0
第 1 次    0    2    0 > 0? 否    0 + 2 = 2    Math.max(0, 2) = 2    从索引 0 出发,最远能到索引 2
第 2 次    1    3    1 > 2? 否    1 + 3 = 4    Math.max(2, 4) = 4    从索引 1 出发,最远能到索引 4(已达终点)
第 3 次    2    1    2 > 4? 否    2 + 1 = 3    Math.max(4, 3) = 4    maxidx 不变
第 4 次    3    1    3 > 4? 否    3 + 1 = 4    Math.max(4, 4) = 4    maxidx 不变
第 5 次    4    4    4 > 4? 否    4 + 4 = 8    Math.max(4, 8) = 8    maxidx 更新
循环结束                        成功遍历完数组,返回 true
示例 2:nums = [3, 2, 1, 0, 4] (失败案例)
循环次数    i (当前索引)    nums[i]    i > maxidx (检查)    i + nums[i] (从当前位置能跳的最远)    maxidx (更新后)    备注
初始状态    -    -    -    -    0    maxidx 初始为 0
第 1 次    0    3    0 > 0? 否    0 + 3 = 3    Math.max(0, 3) = 3    从索引 0 出发,最远能到索引 3
第 2 次    1    2    1 > 3? 否    1 + 2 = 3    Math.max(3, 3) = 3    maxidx 不变
第 3 次    2    1    2 > 3? 否    2 + 1 = 3    Math.max(3, 3) = 3    maxidx 不变
第 4 次    3    0    3 > 3? 否    3 + 0 = 3    Math.max(3, 3) = 3    maxidx 不变,陷入瓶颈
第 5 次    4    4    4 > 3? 是    -    -    触发条件,立即返回 false
从上面的跟踪过程可以清晰地看到,算法是如何高效地判断出结果的。
算法正确性的严谨证明
为什么这个贪心算法是正确的?我们可以用反证法来证明。
假设:贪心算法返回 false,但实际上存在一条从起点到终点的路径。
算法返回 false 的含义:算法在遍历到某个索引 k 时,发现 k > maxidx,于是返回 false。这意味着,根据算法的计算,在索引 k 之前的所有位置,能到达的最远距离都小于 k。
存在有效路径的含义:存在一条路径 p0, p1, p2, ..., pm,其中 p0 = 0 (起点),pm = n-1 (终点),并且对于任意 i>0,都有 p_i <= p_{i-1} + nums[p_{i-1}] (从 p_{i-1} 可以跳到 p_i)。
寻找矛盾点:我们来考察这条有效路径上的第一个 “问题点”。设 p_j 是这条路径上第一个大于算法计算出的 maxidx 的索引。
因为 p0 = 0 不可能大于初始 maxidx (0),所以 j > 0。
路径的前一个点 p_{j-1} 必然小于等于算法在遍历到 p_{j-1} 时计算出的 maxidx。否则,p_{j-1} 就已经是问题点了,与我们的假设矛盾。
当算法遍历到索引 p_{j-1} 时,它会执行更新操作:maxidx = Math.max(maxidx, p_{j-1} + nums[p_{j-1}])。
因为 p_j 是路径上的点,所以 p_j <= p_{j-1} + nums[p_{j-1}]。
由此可知,在算法遍历到 p_{j-1} 之后,maxidx 的值必然大于等于 p_j。
这与我们的假设 “p_j 是第一个大于 maxidx 的点” 相矛盾。
结论:我们的初始假设是错误的。如果贪心算法返回 false,那么必然不存在从起点到终点的有效路径。因此,该算法是正确的。
算法复杂度分析
时间复杂度: O (n)
算法主体是一个 for 循环,它从头到尾遍历了整个数组一次。
循环体内的操作(比较、加法、取最大值)都是常数时间 O (1) 的操作。
因此,总的时间复杂度与数组的长度 n 成正比,即 O (n)。这是该问题的最优时间复杂度,因为你至少需要查看数组中的每个元素一次。
空间复杂度: O (1)
算法在执行过程中,只使用了几个固定的额外变量:i、maxidx。
这些变量的数量不随输入数组 nums 的大小而变化。
因此,算法的空间复杂度是常数级别的,即 O (1)。
与其他算法的对比:贪心 vs. 动态规划
“跳跃游戏” 也可以用动态规划(Dynamic Programming)来解决。
动态规划思路:
定义一个布尔数组 dp,其中 dp[i] 表示 “是否能够到达索引 i”。
** base case **: dp[0] = true,因为我们一开始就在索引 0。
** 状态转移 **: 对于每个索引 i,如果 dp[i] 为 true,那么对于所有 j in [i+1, i+nums[i]],我们都可以到达 j,因此可以将 dp[j] 设为 true。
** 最终结果 **: dp[n-1] 的值。
两种方法对比:
特性    贪心算法 (Greedy)    动态规划 (DP)
时间复杂度    O(n)    O(n^2)
空间复杂度    O(1)    O(n)
核心思想    维护一个全局的 “最远可达” 边界,一次遍历解决问题。    自底向上或自顶向下地计算每个位置 “是否可达”,依赖子问题的解。
实现难度    非常简洁,代码量少。    相对复杂,需要额外空间存储状态。
显然,在这个问题上,贪心算法在时间和空间效率上都远胜于动态规划,是解决该问题的最佳方案。
算法的局限性与适用场景
这个贪心算法专门针对 “跳跃游戏 I” 这种判断可达性的问题。如果问题变种,该算法可能不再适用:
跳跃游戏 II:不仅要判断能否到达,还要计算到达终点的最少跳跃次数。这个问题也可以用贪心算法解决,但思路不同,需要维护 “当前步数能到达的边界” 和 “下一步能到达的边界”。
带限制的跳跃游戏:例如,每次跳跃的长度有上下限,或者某些位置是 “陷阱” 不能踩。这时问题会变得更复杂,可能需要结合 BFS 或动态规划。
总结与启示
“跳跃游戏” 的贪心算法是一个绝佳的例子,它展示了如何将一个看似复杂的问题,通过一个巧妙的 “局部最优” 策略,转化为一个高效、简洁的 “全局最优” 解决方案。
核心启示:
寻找 “贪心量”:解决贪心问题的关键在于找到那个能够代表 “局部最优” 的量。在本题中,这个量就是 “能到达的最远索引” (maxidx)。
证明正确性:在使用贪心算法时,最好能在直觉之外,通过严谨的逻辑(如反证法)证明其正确性,确保它不会在某些特殊情况下失效。
从简单开始:这个算法的实现非常简单,但背后的思想却很深刻。这提醒我们,不要轻视任何一个 “简单” 的算法,深入理解其原理,才能举一反三,解决更复杂的问题。
掌握这个算法,不仅能让你在面试中轻松应对同类问题,更能提升你在面对未知问题时,运用贪心思想进行建模和求解的能力。
class Solution {
    public int jump(int[] nums) {
        if(nums.length==1) return 0;
        int res=0;
        int cur=0;
        int pre=0;
        for(int i=0;i<nums.length;i++){
            cur=Math.max(cur,nums[i]+i);
            if(i==pre){
                res++;
                pre=cur;
                if(pre>=nums.length-1) break;
            }
        }
        return res;
    }
}解释代码,几千字,博客使用
深入解析跳跃游戏 II 的贪心算法:从原理到实现
引言:跳跃游戏 II 的挑战
在算法领域中,"跳跃游戏" 系列问题一直是贪心算法的经典应用场景。其中,"跳跃游戏 II" 相比基础版本更具挑战性:它不仅要求判断是否能到达终点,还需要计算到达终点的最少跳跃次数。
问题描述如下:给定一个非负整数数组 nums,你最初位于数组的第一个位置。数组中的每个元素 nums[i] 表示你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。假设你总能到达数组的最后一个位置。
例如,对于数组 [2,3,1,1,4]:
最优路径是:0 → 1 → 4(跳跃 2 次)
0 → 2 → 3 → 4 虽然能到达终点,但需要 3 次跳跃,不是最优解
本文将深入解析一个基于贪心策略的高效算法,该算法仅用一次遍历即可找到最少跳跃次数,时间复杂度为 O (n),空间复杂度为 O (1)。
算法核心代码
我们先来看这个求解跳跃游戏 II 的最优算法实现:
跳跃游戏II的贪心算法实现
V1
创建时间:21:25
这段代码虽然简洁,但蕴含着精妙的贪心策略。它通过维护两个关键变量(当前最远可达位置和上一次跳跃的边界),在一次遍历中就能计算出最少跳跃次数。
算法设计思想:贪心策略的深度应用
问题分析与核心洞察
跳跃游戏 II 的核心挑战是找到最少跳跃次数。直观来看,我们希望每次跳跃都能尽可能地到达更远的位置,这样才能最大限度地减少跳跃次数。这正是贪心算法的典型应用场景 —— 每一步都做出局部最优选择,最终得到全局最优解。
算法的核心洞察是:在当前跳跃的可达范围内,选择能跳得最远的位置作为下一次跳跃的起点。但我们不需要显式地选择起点,而是通过跟踪 "当前跳跃的边界" 来判断何时需要进行下一次跳跃。
关键变量解析
算法中三个关键变量的作用如下:
res:记录最少跳跃次数,初始值为 0
cur:表示从当前位置及之前所有位置出发,能到达的最远索引
pre:表示上一次跳跃所能到达的最远索引(即当前跳跃的边界)
这三个变量共同协作,实现了对跳跃过程的高效跟踪和控制。
算法逻辑框架
算法的基本流程可以概括为:
遍历数组中的每个位置
不断更新从当前位置能到达的最远位置(cur)
当到达上一次跳跃的边界(i == pre)时:
必须进行一次新的跳跃(res++)
将新的跳跃边界更新为当前能到达的最远位置(pre = cur)
如果新的边界已经覆盖终点,则提前结束
这个逻辑的精妙之处在于,它通过 "边界" 来判断何时需要跳跃,而不是显式地模拟每一次跳跃的过程。
算法详细解析:一步步跟踪
为了深入理解算法的工作原理,我们以示例数组 [2,3,1,1,4] 为例,详细跟踪算法的执行过程:
初始状态
nums = [2,3,1,1,4],数组长度为 5
由于长度大于 1,不触发特殊情况处理
res = 0(初始跳跃次数)
cur = 0(初始最远可达位置)
pre = 0(初始跳跃边界)
遍历过程
循环次数    i (当前索引)    nums[i]    cur (更新后)    i == pre?    res (更新后)    pre (更新后)    备注
1    0    2    max(0, 0+2)=2    0 == 0 → 是    0+1=1    2    第一次跳跃,边界更新为 2
2    1    3    max(2, 1+3)=4    1 == 2 → 否    1    2    未到边界,继续更新最远位置
3    2    1    max(4, 2+1)=4    2 == 2 → 是    1+1=2    4    第二次跳跃,边界更新为 4
4    -    -    -    -    -    -    此时 pre=4,已达到数组最后一个索引 (4),提前退出循环
最终返回 res=2,与预期的最少跳跃次数一致。
让我们再分析另一个案例 [1,1,1,1],看看算法如何处理这种需要多次短跳的情况:
循环次数    i (当前索引)    nums[i]    cur (更新后)    i == pre?    res (更新后)    pre (更新后)    备注
1    0    1    max(0, 0+1)=1    0 == 0 → 是    0+1=1    1    第一次跳跃,边界更新为 1
2    1    1    max(1, 1+1)=2    1 == 1 → 是    1+1=2    2    第二次跳跃,边界更新为 2
3    2    1    max(2, 2+1)=3    2 == 2 → 是    2+1=3    3    第三次跳跃,边界更新为 3
4    3    1    max(3, 3+1)=4    3 == 3 → 是    3+1=4    4    第四次跳跃,此时 pre=4 已超过最后索引 3,退出
最终返回 res=3(注意数组长度为 4,最后索引是 3),正确反映了需要 3 次跳跃才能到达终点。
算法正确性的关键分析
为什么这个算法能够保证得到最少的跳跃次数?我们可以从以下几个方面来理解:
跳跃时机的选择:算法只有在到达上一次跳跃的边界时才进行新的跳跃,这确保了每一次跳跃都是 "不得不跳" 的情况,避免了不必要的跳跃。
最远可达位置的跟踪:通过持续更新 cur 为当前能到达的最远位置,算法实际上是在每一个可能的起点中,选择了能跳得最远的那个作为下一次跳跃的目标。这种选择保证了每一步都尽可能地向前推进,从而最小化跳跃次数。
提前终止条件:当 pre 已经覆盖终点时,算法立即终止,避免了不必要的后续计算。
我们可以用反证法证明该算法的最优性:假设存在比算法计算出的 res 更少的跳跃次数,那么必然存在某一步跳跃的距离超过了算法跟踪的 cur,这与 cur 是 "当前能到达的最远位置" 相矛盾。因此,算法计算出的跳跃次数一定是最少的。
边界情况处理
算法对各种边界情况都有妥善处理:
单元素数组:if(nums.length == 1) return 0 直接返回 0,因为不需要任何跳跃。
一步可达终点:例如 [5,1,1,1,1],第一次跳跃的边界就已经覆盖终点,算法会在第一次跳跃后立即返回 1。
需要跳完所有元素:例如 [1,1,1,1],算法会遍历所有元素直到最后一次跳跃。
最大跳跃覆盖多个位置:例如 [3,1,1,1],第一次跳跃就能覆盖所有位置,算法返回 1。
算法复杂度分析
时间复杂度:O (n)
算法主体是一个单次遍历数组的循环,循环执行次数为数组长度 n。在每次循环中,仅进行了常数时间的操作(比较、加法、赋值等)。因此,总的时间复杂度为 O (n),这是该问题的最优时间复杂度,因为我们至少需要检查每个位置一次。
空间复杂度:O (1)
算法仅使用了三个额外变量(res、cur、pre)以及循环变量i,这些变量的数量与输入数组的大小无关。因此,空间复杂度为 O (1),即常数空间复杂度。
与动态规划等其他可能的解法相比,该贪心算法在时间和空间效率上都具有明显优势。动态规划解法通常需要 O (n²) 的时间复杂度和 O (n) 的空间复杂度,远不如贪心算法高效。
与跳跃游戏 I 的算法对比
跳跃游戏 I(判断能否到达终点)和跳跃游戏 II(计算最少跳跃次数)虽然问题相似,但算法设计有所不同,我们可以通过对比加深理解:
特性    跳跃游戏 I    跳跃游戏 II
核心目标    判断可达性    计算最少跳跃次数
关键变量    一个最远可达位置 maxidx    两个变量:当前最远 cur 和上一次边界 pre
终止条件    1. 到达终点 → 返回 true
2. 当前索引超过 maxidx → 返回 false    当 pre 覆盖终点时终止
核心逻辑    检查是否能到达当前索引    跟踪跳跃边界,在必要时进行跳跃
两种算法都运用了贪心思想,但跳跃游戏 II 需要更精细的边界管理来计算跳跃次数。
实际应用场景
跳跃游戏 II 的算法思想虽然源于一个抽象的算法问题,但在实际生活中也有类似的应用场景:
资源分配优化:在有限资源下,如何通过最少的步骤覆盖所有目标点。
路径规划:机器人或无人机在复杂环境中,如何规划最少步数的移动路径。
网络路由:在网络中,数据包如何通过最少的跳转次数到达目的地。
任务调度:如何通过最少的任务切换次数完成所有任务。
这些场景都可以借鉴 "每次选择最优下一步" 的贪心思想,以达到效率最大化的目标。
算法设计的启示
跳跃游戏 II 的贪心算法给我们带来了以下启示:
局部最优到全局最优:贪心算法的核心是找到合适的局部最优策略,使其能够累积为全局最优解。在本题中,"每次跳跃到最远可达位置" 就是这样一种策略。
状态压缩:通过巧妙设计状态变量(如cur和pre),可以大幅降低算法的空间复杂度,从 O (n) 优化到 O (1)。
边界控制:在涉及范围或边界的问题中,跟踪和更新边界往往是解题的关键。
提前终止:合理设置提前终止条件,可以显著提高算法的实际运行效率,尤其是在处理大规模数据时。
掌握这些思想,有助于我们在面对新问题时设计出更高效的算法。
总结
跳跃游戏 II 的贪心算法是一个将复杂问题简单化的典范。它通过维护当前最远可达位置和上一次跳跃边界两个关键变量,在一次遍历中就计算出了到达终点的最少跳跃次数,实现了 O (n) 的时间复杂度和 O (1) 的空间复杂度。
算法的核心思想是:每当到达上一次跳跃的边界时,就必须进行一次新的跳跃,并将新的边界设置为当前能到达的最远位置。这种策略确保了每一次跳跃都是必要的且最优的,从而保证了总的跳跃次数最少。
通过深入理解这个算法,我们不仅能够掌握解决跳跃游戏类问题的方法,更能体会到贪心算法的精髓 —— 如何通过局部最优选择实现全局最优解。这种思想在许多优化问题中都有广泛应用,是算法设计中的重要工具。
无论是算法初学者还是有经验的开发者,理解和掌握这个算法都将有助于提升算法设计和问题解决能力,为应对更复杂的挑战奠定基础。
 

总结:

本文深入解析了跳跃游戏II的贪心算法解决方案。该算法通过维护当前最远可达位置和跳跃边界两个关键变量,在一次遍历中计算出最少跳跃次数,实现了O(n)时间复杂度和O(1)空间复杂度。算法的核心在于:每当到达当前跳跃边界时进行新跳跃,并将边界更新为当前能到达的最远位置。这种策略确保了每次跳跃都是必要且最优的,从而保证总跳跃次数最少。文章通过实例跟踪、正确性证明和复杂度分析,展示了该贪心算法的高效性和可靠性,并与其他解法进行了对比,凸显了其优越性。该算法不仅解决了特定问题,更体现了贪心算法&quot;局部最优到全局

http://www.dtcms.com/a/404442.html

相关文章:

  • pc端宣传网站开发做网站 怎么赚钱吗
  • Nginx 特性、配置与实战部署
  • 新乡微网站建设青岛网站建设铭盛信息
  • 建设信用卡在线海淘网站返现电子商务网站建设php
  • 企业网站页面甘洛网站建设
  • 网站开发解决方案wordpress打字不显示
  • 个人网站类型王烨超
  • 别人公司网站进不去个性化网站设计
  • 阿里云网站怎么备案域名解析网站建设与管理自考重点
  • 网站关键词描述字数凡客小程序官方
  • 松江手机网站开发wordpress 标签链接地址
  • 中国石油工程建设有限公司网站宜宾建设局网站
  • photoshop制作网站海报一般网站建设电话
  • 教做视频的网站优购物官方网站app
  • 高校校园网站建设与运行go生物网站做蛋白定位
  • 各大网站开发的区块链123上网
  • 做网站都需要建哪些文件夹长春是几线城市2020排名
  • 杭州网站建设seo网络广告营销案例有哪些
  • 网站开发注册个体工商广南网站建设
  • 网站建设小江网页设计用哪个网站做简历更好
  • 大连网站设计公司百度标注平台怎么加入
  • 佛山外贸建站免费企业建站模板
  • 企业网站和信息化建设金蝶搜索引擎技术优化
  • 【Proteus仿真】AT89C51单片机串行数据转换为并行仿真
  • 做动态图网站有哪些专业seo优化推广
  • 域名如何做网站策划公司取名字大全
  • Linux 虚拟机软件 VMware Workstation Pro 安装CentOS的相关说明和操作
  • 关于网站建设的参考文献做一些网站犯法么
  • 个人网站可以如果做淘宝客成都做网站设
  • 深圳做网站的好公司有哪些网站开发的实训周