您的位置:首页 > 汽车 > 时评 > 网站建设中的服务器搭建方式_工程公司名称大全大气好听_东莞网络推广公司_广西seo公司

网站建设中的服务器搭建方式_工程公司名称大全大气好听_东莞网络推广公司_广西seo公司

2025/2/25 22:50:20 来源:https://blog.csdn.net/m0_55049655/article/details/144959979  浏览:    关键词:网站建设中的服务器搭建方式_工程公司名称大全大气好听_东莞网络推广公司_广西seo公司
网站建设中的服务器搭建方式_工程公司名称大全大气好听_东莞网络推广公司_广西seo公司

贪心算法详解(JavaScript)(局部最优->全局最优)

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下的最优选择(局部最优)的算法设计方法。通过局部最优解的累积,试图最终达到全局最优解。尽管贪心算法并不总能保证得到最优解,但它对某些问题(如优化类问题)非常有效。

贪心算法的核心思想
  1. 问题分解:将问题分解成多个子问题。
  2. 贪心选择性质:对每个子问题,做出一个局部最优选择。
  3. 无后效性:当前的选择不会影响后续的决策,或者即使受到影响,也不会导致最优解的丢失。
  4. 重复上述步骤:直到所有子问题解决。
贪心算法的实现步骤
  1. 分析问题是否适合贪心:问题是否满足贪心选择性质和无后效性。
  2. 构造贪心策略:确定如何在每一步选择局部最优解。
  3. 验证贪心策略的正确性:证明或推导出贪心策略能够得到问题的最优解。
  4. 实现代码:利用循环、排序、优先队列等工具编写代码。

经典贪心算法案例及实现

案例 1:分发饼干

问题描述
有两组数据,分别是孩子的胃口数组 g 和饼干大小数组 s。每个孩子只能吃一个饼干,只有当饼干的大小 ≥ 孩子的胃口时,该饼干才能满足该孩子。求最多有多少孩子可以满足。

贪心思路

  1. 将孩子的胃口和饼干大小排序。
  2. 每次将最小的饼干分配给最小胃口的孩子(局部最优)。
  3. 如果当前饼干不能满足孩子,则尝试下一块饼干。

代码实现

function findContentChildren(g, s) {// 排序胃口和饼干大小g.sort((a, b) => a - b);s.sort((a, b) => a - b);let child = 0;let cookie = 0;while (child < g.length && cookie < s.length) {if (s[cookie] >= g[child]) {// 当前饼干可以满足当前孩子child++;}// 尝试分配下一块饼干cookie++;}return child;
}// 示例
console.log(findContentChildren([1, 2, 3], [1, 1])); // 输出: 1
console.log(findContentChildren([1, 2], [1, 2, 3])); // 输出: 2

案例 2:跳跃游戏

问题描述
给定一个非负整数数组,每个元素表示你在该位置最多能跳多远。判断是否可以从数组的第一个位置跳到最后一个位置。

贪心思路

  1. 维护一个变量 maxReach 表示当前能够到达的最远位置。
  2. 遍历数组:
    • 如果当前索引大于 maxReach,则无法跳到当前位置。
    • 否则更新 maxReach 为当前索引加上跳跃步数。
  3. 如果遍历结束时 maxReach 大于等于数组最后一个位置,则说明可以到达。

代码实现

function canJump(nums) {let maxReach = 0;for (let i = 0; i < nums.length; i++) {if (i > maxReach) {// 当前索引无法到达return false;}maxReach = Math.max(maxReach, i + nums[i]);}return true;
}// 示例
console.log(canJump([2, 3, 1, 1, 4])); // 输出: true
console.log(canJump([3, 2, 1, 0, 4])); // 输出: false

案例 3:区间调度问题

问题描述
给定多个区间,求最多能选择不重叠的区间数量。

贪心思路

  1. 按照区间的结束时间升序排序(局部最优:优先选择结束时间最早的区间)。
  2. 遍历区间列表,每次选择当前区间与上一次选择的区间不重叠的区间。

代码实现

function intervalSchedule(intervals) {if (intervals.length === 0) return 0;// 按结束时间升序排序intervals.sort((a, b) => a[1] - b[1]);let count = 1; // 至少有一个区间被选中let end = intervals[0][1];for (let i = 1; i < intervals.length; i++) {if (intervals[i][0] >= end) {// 当前区间与上一个区间不重叠count++;end = intervals[i][1];}}return count;
}// 示例
console.log(intervalSchedule([[1, 3], [2, 4], [3, 5]])); // 输出: 2
console.log(intervalSchedule([[1, 2], [2, 3], [3, 4], [1, 3]])); // 输出: 3

贪心算法的优缺点

优点

  • 简单高效:解决问题的步骤清晰,易于实现。
  • 局部最优:很多时候可以快速找到接近最优的解。

缺点

  • 适用性有限:不适用于所有问题,特别是需要全局视野的问题。
  • 无法回溯:一旦做出选择,就不能回退检查其他可能性。

贪心算法适用场景

  1. 贪心选择性质:整体最优解可以通过一系列局部最优解构成
  2. 无后效性:某一步的决策不会影响后续步骤的最优性。

常见的适用问题包括:最小生成树(Prim、Kruskal 算法)、最短路径问题(Dijkstra 算法)、区间调度、活动选择等。

希望这些内容对你理解和使用贪心算法有所帮助! 😊

贪心算法vs动态规划算法

贪心算法与动态规划算法的比较详解

贪心算法和动态规划是两种常用的算法设计方法,它们各自适用于不同类型的问题,主要区别在于解决问题的思路和适用场景。以下是两者的详细比较:


1. 核心思想
方面贪心算法动态规划
基本思路每一步都选择当前状态下的局部最优解。将问题分解成多个子问题,利用子问题的解构造全局最优解。
全局性通过局部最优推导全局最优解。通过递推式逐步解决子问题以达到全局最优解。

2. 适用问题
方面贪心算法动态规划
问题类型适用于具有贪心选择性质和无后效性的优化问题。适用于有重叠子问题和最优子结构性质的问题。
典型问题活动选择、最小生成树、最短路径(Dijkstra)。背包问题、最长公共子序列、最长递增子序列。

3. 实现复杂度
方面贪心算法动态规划
实现难度相对简单,直接选择当前最优解即可。需要设计状态、递推关系和表格存储。
时间复杂度通常为 ( O(n \log n) ) 或 ( O(n) )。通常为 ( O(n^2) ) 或更高。
空间复杂度一般为 ( O(1) )。需要额外的存储空间(例如表格),通常为 ( O(n^2) )。

4. 解决过程
方面贪心算法动态规划
过程描述通过排序、优先队列等工具选择局部最优。使用状态转移方程递推计算,通常自底向上。
回溯性无法回溯,一旦选择即固定。可以通过存储子问题结果进行回溯。

5. 优势与局限性
方面贪心算法动态规划
优势高效、简单,适用于实时计算。可以保证得到全局最优解。
局限性不保证全局最优解,适用问题较少。复杂度较高,消耗更多的时间和空间资源。

贪心算法与动态规划的对比表格总结

属性贪心算法动态规划
选择方式每一步选择局部最优解依赖子问题结果,通过递推求解全局最优解
适用问题特性贪心选择性质、无后效性重叠子问题、最优子结构
时间复杂度通常较低通常较高
空间复杂度通常为 ( O(1) )需要额外存储,通常为 ( O(n^2) )
实现难度简单,逻辑清晰需要设计状态转移方程,难度较高
回溯性不可回溯可通过记录表格结果回溯
保证最优解不一定(除非证明问题适合贪心)一定能保证最优解
典型应用活动选择、最小生成树、最短路径背包问题、最长公共子序列、斐波那契数列

两种算法的使用场景总结

  1. 贪心算法

    • 问题适合贪心选择,且有无后效性。
    • 需要快速计算的近似解或简单解。
    • 例子:找零问题、最小生成树、区间调度。
  2. 动态规划

    • 问题有重叠子问题和最优子结构。
    • 不确定贪心是否适用,但需要保证最优解。
    • 例子:背包问题、最长公共子序列、股票买卖问题。

总结:贪心算法和动态规划在解决问题时各有侧重。对于简单、高效的场景可以优先考虑贪心算法,而对于复杂、全局优化的问题则更适合动态规划。

摆动序列

在这里插入图片描述
在这里插入图片描述

核心:考虑这三种情况!!!

在这里插入图片描述
在这里插入图片描述

/*** @param {number[]} nums* @return {number}*/
var wiggleMaxLength = function(nums) {// let len=nums.length;// let strLen=0;// let preDiff=0,curDiff=0,count=0;// //判断字符序列是否为长度,是偶数的话// //还需比较最后两个数组的大小是否大于0// let flag=nums.length%2;// for(let i=0,j=1;j<nums.length;i++,j++){//     curDiff=nums[j]-nums[i];//     if(preDiff>0&&curDiff<0){//         count++;//     }//     if(j===nums.length-1&&!flag&&curDiff>0){//         strLen=count>=1?2*count+2:2;//     }else{//         strLen=count>=1?2*count+1:2;//     }//     preDiff=curDiff;// }// return  strLen;//考虑平坡、单调平坡和收尾元素//默认尾部元素有个坡//preDiff=0可起到前方有个和开始元素值一样的虚拟元素if(nums.length<=1)return nums.length;let preDiff=0,curDiff=0,res=1;//i<nums.length-1  默认尾部元素有个坡for(let i=0;i<nums.length-1;i++){curDiff=nums[i+1]-nums[i];if((curDiff>0&&preDiff<=0)||(curDiff<0&&preDiff>=0)){res++;//在这赋值curDiff,表示只有坡度方向变化时才更新preDiff//防止在单调平坡上出现errorpreDiff=curDiff;}}return res;
};

最大子序列和

在这里插入图片描述
在这里插入图片描述

核心:怎样用代码重新开始位置,其实此题返回值,直接将前面累加和赋值0就行! if(curSum<0)curSum=0;

/*** @param {number[]} nums* @return {number}*/
var maxSubArray = function(nums) {// let res=-Infinity;let res=[];for(let i=0;i<nums.length;i++){curSum+=nums[i];if(curSum>res)res=curSum;//!!!核心,更换起始位置就是把当前curSum赋值为0就行!if(curSum<0)curSum=0;}return res; // for(let i=0;i<nums.length;i++){//     let curSum=0;//     for(let j=i;j<nums.length;j++){//         curSum+=nums[j];//         res.push(curSum);//     }// }// let maxArr=res.sort((a,b)=>b-a)[0];// return maxArr;};

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com