范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

95的算法都是基于这6种算法思想

  写在前面
  因为本次文章篇幅较长,所以为了便利大家查看今日最新职位信息,所以将内容上移至顶部,忘各位大大海涵
  职位
  要求算法交易工程师
  薪资:20k-40k
  工作位置:上海 1. 有进行算法开发的经验,有股票,期货等金融行业基本知识,了解经典量化策略加分;
  2. 知名院校本科及以上学历,计算机、物理、数学、统计等相关理工类专业;0-5年工作经验
  3. C++/Rust/Go/Python至少一种语言的多年工作经验;
  4. 团队的技术栈未来会以Rust为主;
  5. 优秀的数据结构和算法基础; 6. 有高频交易相关工作经验,工程或者策略加分。  量化研究员
  薪资:20k-40k
  工作位置:上海 1.硕士及以上学历,0-3年工作经验,统计、金融工程、数学、物理、计算机等相关专业优先。
  2.拥有股票多因子、CTA策略研究、机器学习、深度学习项目中的一种或多种经验。
  3.具备扎实的数理逻辑能力和统计学基础。高中阶段理科竞赛、各类大学生建模竞赛及机器学习竞赛中有获奖经历者优先。
  4.具备扎实的编程能力,熟练使用python进行数据处理、数据分析和策略开发。熟练掌握C++/Linux/Rust者优先。
  5.对量化策略研究有浓厚兴趣,热爱探索,注重细节。  量化研发工程师
  薪资:20k-40k
  工作城市:上海 1. 统招本科或以上学历,具备扎实的编程基础和数据结构算法基础,优秀的编程能力和问题解决能力;
  2. 对经验不设要求,应届生也考虑,具备核心业务系统或负责业务系统架构设计开发经验加分;
  3. 对工程化有较好理解,对网络、自动化测试、安全、性能有较好理解;
  4.不对目前使用语言过多要求,热爱技术、喜欢钻研技术、持续学习成长;
  投递方式
  通过Freemen APP选择城市并搜索职位名称
  算法思想是解决问题的核心,万丈高楼起于平地,在算法中也是如此,95% 的算法都是基于这 6 种算法思想,结下了介绍一下这 6 种算法思想,帮助你理解及解决各种算法问题。  1 递归算法
  1.1 算法策略
  递归算法是一种直接或者间接调用自身函数或者方法的算法。
  递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。递归算法对解决一大类问题很有效,它可以使算法简洁和易于理解。
  优缺点:  优点:实现简单易上手  缺点:递归算法对常用的算法如普通循环等,运行效率较低;并且在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,递归太深,容易发生栈溢出
  1.2 适用场景
  递归算法一般用于解决三类问题:  数据的定义是按递归定义的。(斐波那契数列)  问题解法按递归算法实现。(回溯)  数据的结构形式是按递归定义的。(树的遍历,图的搜索)
  递归的解题策略:  第一步:明确你这个函数的输入输出,先不管函数里面的代码什么,而是要先明白,你这个函数的输入是什么,输出为何什么,功能是什么,要完成什么样的一件事。  第二步:寻找递归结束条件,我们需要找出什么时候递归结束,之后直接把结果返回  第三步:明确递归关系式,怎么通过各种递归调用来组合解决当前问题
  1.3 使用递归算法求解的一些经典问题  斐波那契数列  汉诺塔问题  树的遍历及相关操作
  DOM树为例
  下面以以 DOM 为例,实现一个  document.getElementById   功能
  由于DOM是一棵树,而树的定义本身就是用的递归定义,所以用递归的方法处理树,会非常地简单自然。
  第一步:明确你这个函数的输入输出
  从 DOM 根节点一层层往下递归,判断当前节点的 id 是否是我们要寻找的  id="d-cal"
  输入:DOM 根节点  document   ,我们要寻找的 id="d-cal"
  输出:返回满足  id="sisteran"   的子结点 function getElementById(node, id){}
  第二步:寻找递归结束条件
  从document开始往下找,对所有子结点递归查找他们的子结点,一层一层地往下查找:  如果当前结点的 id 符合查找条件,则返回当前结点  如果已经到了叶子结点了还没有找到,则返回 null  function getElementById(node, id){     // 当前结点不存在,已经到了叶子结点了还没有找到,返回 null     if(!node) return null     // 当前结点的 id 符合查找条件,返回当前结点     if(node.id === id) return node }
  第三步:明确递归关系式
  当前结点的 id 不符合查找条件,递归查找它的每一个子结点  function getElementById(node, id){     // 当前结点不存在,已经到了叶子结点了还没有找到,返回 null     if(!node) return null     // 当前结点的 id 符合查找条件,返回当前结点     if(node.id === id) return node     // 前结点的 id 不符合查找条件,继续查找它的每一个子结点     for(var i = 0; i < node.childNodes.length; i++){         // 递归查找它的每一个子结点         var found = getElementById(node.childNodes[i], id);         if(found) return found;     }     return null; }
  就这样,我们的一个  document.getElementById   功能已经实现了: function getElementById(node, id){     if(!node) return null;     if(node.id === id) return node;     for(var i = 0; i < node.childNodes.length; i++){         var found = getElementById(node.childNodes[i], id);         if(found) return found;     }     return null; } getElementById(document, "d-cal");
  最后在控制台验证一下,执行结果如下图所示:
  使用递归的优点是代码简单易懂,缺点是效率比不上非递归的实现。Chrome浏览器的查DOM是使用非递归实现。非递归要怎么实现呢?
  如下代码:  function getByElementId(node, id){     //遍历所有的Node     while(node){         if(node.id === id) return node;         node = nextElement(node);     }     return null; }
  还是依次遍历所有的  DOM   结点,只是这一次改成一个 while   循环,函数 nextElement   负责找到下一个结点。所以关键在于这个 nextElement   如何实现非递归查找结点功能: // 深度遍历 function nextElement(node){     // 先判断是否有子结点     if(node.children.length) {         // 有则返回第一个子结点         return node.children[0];     }     // 再判断是否有相邻结点     if(node.nextElementSibling){         // 有则返回它的下一个相邻结点         return node.nextElementSibling;     }     // 否则,往上返回它的父结点的下一个相邻元素,相当于上面递归实现里面的for循环的i加1     while(node.parentNode){         if(node.parentNode.nextElementSibling) {             return node.parentNode.nextElementSibling;         }         node = node.parentNode;     }     return null; }
  在控制台里面运行这段代码,同样也可以正确地输出结果。不管是非递归还是递归,它们都是深度优先遍历,这个过程如下图所示。
  实际上 getElementById 浏览器是用的一个哈希 map 存储的,根据 id 直接映射到 DOM 结点,而 getElementsByClassName 就是用的这样的非递归查找。
  参考:我接触过的前端数据结构与算法  2 分治算法
  2.1 算法策略
  在计算机科学中,分治算法是一个很重要的算法,快速排序、归并排序等都是基于分治策略进行实现的,所以,建议理解掌握它。
  分治,顾名思义,就是  分而治之  ,将一个复杂的问题,分成两个或多个相似的子问题,在把子问题分成更小的子问题,直到更小的子问题可以简单求解,求解子问题,则原问题的解则为阿子问题解的合并。
  2.2 适用场景
  当出现满足以下条件的问题,可以尝试只用分治策略进行求解:  原始问题可以分成多个相似的子问题  子问题可以很简单的求解  原始问题的解是子问题解的合并  各个子问题是相互独立的,不包含相同的子问题
  分治的解题策略:  第一步:分解,将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题  第二步:解决,解决各个子问题  第三步:合并,将各个子问题的解合并为原问题的解
  2.3使用分治法求解的一些经典问题  二分查找  归并排序  快速排序  汉诺塔问题  React 时间分片
  二分查找
  也称折半查找算法,它是一种简单易懂的快速查找算法。例如我随机写0-100之间的一个数字,让你猜我写的是什么?你每猜一次,我就会告诉你猜的大了还是小了,直到猜中为止。
  第一步:分解
  每次猜拳都把上一次的结果分出大的一组和小的一组,两组相互独立  选择数组中的中间数  function binarySearch(items, item) {     // low、mid、high将数组分成两组     var low = 0,         high = items.length - 1,         mid = Math.floor((low+high)/2),         elem = items[mid]     // ... }
  第二步:解决子问题
  查找数与中间数对比  比中间数低,则去中间数左边的子数组中寻找;  比中间数高,则去中间数右边的子数组中寻找;  相等则返回查找成功  while(low <= high) {  if(elem < item) { // 比中间数高   low = mid + 1  } else if(elem > item) { // 比中间数低   high = mid - 1  } else { // 相等      return mid  } }
  第三步:合并  function binarySearch(items, item) {     var low = 0,         high = items.length - 1,         mid, elem     while(low <= high) {         mid = Math.floor((low+high)/2)         elem = items[mid]         if(elem < item) {             low = mid + 1         } else if(elem > item) {             high = mid - 1         } else {             return mid         }     }     return -1 }
  最后,二分法只能应用于数组有序的情况,如果数组无序,二分查找就不能起作用了  function binarySearch(items, item) {     // 快排     quickSort(items)     var low = 0,         high = items.length - 1,         mid, elem     while(low <= high) {         mid = Math.floor((low+high)/2)         elem = items[mid]         if(elem < item) {             low = mid + 1         } else if(elem > item) {             high = mid - 1         } else {             return mid         }     }     return -1 }  // 测试 var arr = [2,3,1,4] binarySearch(arr, 3) // 2  binarySearch(arr, 5) // -1
  测试成功
  3 贪心算法
  3.1 算法策略
  贪心算法,故名思义,总是做出当前的最优选择,即期望通过局部的最优选择获得整体的最优选择。
  某种意义上说,贪心算法是很贪婪、很目光短浅的,它不从整体考虑,仅仅只关注当前的最大利益,所以说它做出的选择仅仅是某种意义上的局部最优,但是贪心算法在很多问题上还是能够拿到最优解或较优解,所以它的存在还是有意义的。
  3.2 适用场景
  在日常生活中,我们使用到贪心算法的时候还是挺多的,例如:
  从100章面值不等的钞票中,抽出 10 张,怎样才能获得最多的价值?
  我们只需要每次都选择剩下的钞票中最大的面值,最后一定拿到的就是最优解,这就是使用的贪心算法,并且最后得到了整体最优解。
  但是,我们任然需要明确的是,期望通过局部的最优选择获得整体的最优选择,仅仅是期望而已,也可能最终得到的结果并不一定不能是整体最优解。
  例如:求取A到G最短路径:
  根据贪心算法总是选择当前最优选择,所以它首先选择的路径是 AB,然后 BE、EG,所得到的路径总长为 1 + 5 + 4 = 10,然而这并不是最短路径,最短路径为 A->C->G : 2 + 2 = 4,所以说,贪心算法得到得并不一定是最优解。
  那么一般在什么时候可以尝试选择使用贪心算法喃?
  当满足一下条件时,可以使用:  原问题复杂度过高  求全局最优解的数学模型难以建立或计算量过大  没有太大必要一定要求出全局最优解,"比较优"就可以
  如果使用贪心算法求最优解,可以按照以下  步骤求解  : 首先,我们需要明确什么是最优解(期望)  然后,把问题分成多个步骤,每一步都需要满足:
  可行性:每一步都满足问题的约束
  局部最优:每一步都做出一个局部最优的选择  - 不可取消:选择一旦做出,在后面遇到任何情况都不可取消  最后,叠加所有步骤的最优解,就是全局最优解
  3.3 经典案例:活动选择问题
  使用贪心算法求解的经典问题有:  最小生成树算法  单源最短路径的 Dijkstra 算法  Huffman 压缩编码  背包问题  活动选择问题等
  其中活动选择问题是最简单的,这里详细介绍这个。
  活动选择问题是《算法导论》上的例子,也是一个非常经典的问题。有 n 个活动(a1,a2,…,an)需要使用同一个资源(例如教室),资源在某个时刻只能供一个活动使用。每个活动 ai 都有一个开始时间 si 和结束时间 fi 。一旦被选择后,活动 ai 就占据半开时间区间 [si,fi) 。如果 [si,fi) 和 [sj,fj) 互不重叠,ai 和 aj 两个活动就可以被安排在这一天。
  该问题就是要安排这些活动,使得尽量多的活动能不冲突的举行。例如下图所示的活动集合S,其中各项活动按照结束时间单调递增排序。
  共有 7 个活动,它们在 18 个小时内需要占用的时间如上图,如何选择活动,能让这间教室利用率最高喃(能够举行更多的活动)?
  贪心算法对这种问题的解决很简单的,它开始时刻开始选择,每次选择开始时间与与已选择活动不冲突的,结束时间又比较靠前的活动,这样会让剩下的时间区间更长。
  首先 a1 活动的结束时间最早,选择 a1 活动  a1 结束后,a2 有时间冲突,不可选择,a3、a4 都可选择,但 a4 结束时间最早,选择 a4  依次选择时间没有冲突的,又结束时间最早的活动
  最终选择活动为 a1,a4,a5,a7。为最优解。
  4 回溯算法
  4.1 算法策略
  回溯算法是一种搜索法,试探法,它会在每一步做出选择,一旦发现这个选择无法得到期望结果,就回溯回去,重新做出选择。深度优先搜索利用的就是回溯算法思想。
  4.2 适用场景
  回溯算法很简单,它就是不断的尝试,直到拿到解。它的这种算法思想,使它通常用于解决广度的搜索问题,即从一组可能的解中,选择一个满足要求的解。
  4.3 使用回溯算法的经典案例  深度优先搜索  0-1背包问题  正则表达式匹配  八皇后  数独  全排列
  等等,深度优先搜索我们在图那一章已经介绍过,这里以正则表达式匹配为例,介绍一下
  正则表达式匹配var string = "abbc"
  var regex = /ab{1,3}c/
  console.log( string.match(regex) )
  // ["abbc", index: 0, input: "abbc", groups: undefined]
  它的匹配过程:
  在第 5 步匹配失败,此时  b{1,3}   已经匹配到了两个 b   正在尝试第三个 b   ,结果发现接下来是 c   。此时就需要回溯到上一步, b{1,3}   匹配完毕(匹配到了 bb   ),然后再匹配 c   ,匹配到了 c   匹配结束。
  5 动态规划
  5.1 算法策略
  动态规划也是将复杂问题分解成小问题求解的策略,与分治算法不同的是,分治算法要求各子问题是相互独立的,而动态规划各子问题是相互关联的。
  所以,动态规划适用于子问题重叠的情况,即不同的子问题具有公共的子子问题,在这种情况下,分治策略会做出很多不必要的工作,它会反复求解那些公共子子问题,而动态规划会对每个子子问题求解一次,然后保存在表格中,如果遇到一致的问题,从表格中获取既可,所以它无需求解每一个子子问题,避免了大量的不必要操作。
  5.2 适用场景
  动态规划适用于求解最优解问题,比如,从面额不定的100个硬币中任意选取多个凑成10元,求怎样选取硬币才可以使最后选取的硬币数最少又刚好凑够了10元。这就是一个典型的动态规划问题。它可以分成一个个子问题(每次选取硬币),每个子问题又有公共的子子问题(选取硬币),子问题之间相互关联(已选取的硬币总金额不能超过10元),边界条件就是最终选取的硬币总金额为 10 元。
  针对上例,也许你也可以说,我们可以使用回溯算法,不断的去试探,但回溯算法是使用与求解广度的解(满足要求的解),如果是用回溯算法,我们需要尝试去找所有满足条件的解,然后找到最优解,时间复杂度为 O(2^n^) ,这性能是相当差的。大多数适用于动态规划的问题,都可以使用回溯算法,只是使用回溯算法的时间复杂度比较高而已。
  最后,总结一下,我们使用动态规划求解问题时,需要遵循以下几个重要步骤:  定义子问题  实现需要反复执行解决的子子问题部分  识别并求解出边界条件
  5.3 使用动态规划求解的一些经典问题  爬楼梯问题:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?  背包问题:给出一些资源(有总量及价值),给一个背包(有总容量),往背包里装资源,目标是在背包不超过总容量的情况下,装入更多的价值  硬币找零:给出面额不定的一定数量的零钱,以及需要找零的钱数,找出有多少种找零方案  图的全源最短路径:一个图中包含 u、v 顶点,找出从顶点 u 到顶点 v 的最短路径  最长公共子序列:找出一组序列的最长公共子序列(可由另一序列删除元素但不改变剩下元素的顺序实现)
  这里以最长公共子序列为例。
  爬楼梯问题
  这里以动态规划经典问题爬楼梯问题为例,介绍求解动态规划问题的步骤。
  第一步:定义子问题
  如果用  dp[n]   表示第 n   级台阶的方案数,并且由题目知:最后一步可能迈 2 个台阶,也可迈 1 个台阶,即第 n   级台阶的方案数等于第 n-1   级台阶的方案数加上第 n-2   级台阶的方案数
  第二步:实现需要反复执行解决的子子问题部分  dp[n] = dp[n−1] + dp[n−2]
  第三步:识别并求解出边界条件  // 第 0 级 1 种方案  dp[0]=1  // 第 1 级也是 1 种方案  dp[1]=1
  最后一步:把尾码翻译成代码,处理一些边界情况  let climbStairs = function(n) {     let dp = [1, 1]     for(let i = 2; i <= n; i++) {         dp[i] = dp[i - 1] + dp[i - 2]     }     return dp[n] }
  复杂度分析:  时间复杂度:O(n)  空间复杂度:O(n)
  优化空间复杂度:  let climbStairs = function(n) {     let res = 1, n1 = 1, n2 = 1     for(let i = 2; i <= n; i++) {         res = n1 + n2         n1 = n2         n2 = res     }     return res }
  空间复杂度:O(1)
  6 枚举算法
  6.1 算法策略
  枚举算法的思想是:将问题的所有可能的答案一一列举,然后根据条件判断此答案是否合适,保留合适的,丢弃不合适的。
  6.2 解题思路  确定枚举对象、枚举范围和判定条件。  逐一列举可能的解,验证每个解是否是问题的解。
  7 刷题
  7.1 爬楼梯问题
  假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
  每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
  注意:  给定 n 是一个正整数。
  示例 1:  输入: 2 输出: 2 解释: 有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶
  示例 2:  输入: 3 输出: 3 解释: 有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶
  解法:动态规划
  动态规划(Dynamic Programming,DP)是一种将复杂问题分解成小问题求解的策略,但与分治算法不同的是,分治算法要求各子问题是相互独立的,而动态规划各子问题是相互关联的。
  分治,顾名思义,就是分而治之,将一个复杂的问题,分成两个或多个相似的子问题,在把子问题分成更小的子问题,直到更小的子问题可以简单求解,求解子问题,则原问题的解则为子问题解的合并。
  我们使用动态规划求解问题时,需要遵循以下几个重要步骤:  定义子问题  实现需要反复执行解决的子子问题部分  识别并求解出边界条件
  第一步:定义子问题
  如果用  dp[n]   表示第 n   级台阶的方案数,并且由题目知:最后一步可能迈 2 个台阶,也可迈 1 个台阶,即第 n   级台阶的方案数等于第 n-1   级台阶的方案数加上第 n-2   级台阶的方案数
  第二步:实现需要反复执行解决的子子问题部分  dp[n] = dp[n−1] + dp[n−2]
  第三步:识别并求解出边界条件  // 第 0 级 1 种方案  dp[0]=1  // 第 1 级也是 1 种方案  dp[1]=1
  最后一步:把尾码翻译成代码,处理一些边界情况  let climbStairs = function(n) {     let dp = [1, 1]     for(let i = 2; i <= n; i++) {         dp[i] = dp[i - 1] + dp[i - 2]     }     return dp[n] }
  复杂度分析:  时间复杂度:O(n)  空间复杂度:O(n)
  优化空间复杂度:  let climbStairs = function(n) {     let res = 1, n1 = 1, n2 = 1     for(let i = 2; i <= n; i++) {         res = n1 + n2         n1 = n2         n2 = res     }     return res }
  空间复杂度:O(1)
  7.2 使用最小花费爬楼梯
  数组的每个索引作为一个阶梯,第  i   个阶梯对应着一个非负数的体力花费值 cost[i]   (索引从0开始)。
  每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。
  您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。
  示例 1:  输入: cost = [10, 15, 20] 输出: 15 解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。
  示例 2:  输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] 输出: 6 解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。
  注意:  cost   的长度将会在 [2, 1000]   。 每一个  cost[i]   将会是一个Integer类型,范围为 [0, 999]   。
  解法:动态规划
  本题注意理解题意:  第  i   级台阶是第 i-1   级台阶的阶梯顶部。 踏上第  i   级台阶花费 cost[i]   ,直接迈一大步跨过而不踏上去则不用花费。 楼梯顶部在数组之外,如果数组长度为  len  ,那么楼顶就在下标为 len
  第一步:定义子问题
  踏上第  i   级台阶的体力消耗为到达前两个阶梯的最小体力消耗加上本层体力消耗: 最后迈 1 步踏上第  i   级台阶:dp[i-1] + cost[i]   最后迈 1 步踏上第  i   级台阶:dp[i-2] + cost[i]
  第二步:实现需要反复执行解决的子子问题部分
  所以踏上第  i   级台阶的最小花费为: dp[i] = min(dp[i-2], dp[i-1]) + cost[i]
  第三步:识别并求解出边界条件  // 第 0 级 cost[0] 种方案  dp[0] = cost[0] // 第 1 级,有两种情况 // 1:分别踏上第0级与第1级台阶,花费cost[0] + cost[1] // 2:直接从地面开始迈两步直接踏上第1级台阶,花费cost[1] dp[1] = min(cost[0] + cost[1], cost[1]) = cost[1]
  最后一步:把尾码翻译成代码,处理一些边界情况  let minCostClimbingStairs = function(cost) {     cost.push(0)     let dp = [], n = cost.length     dp[0] = cost[0]     dp[1] = cost[1]     for(let i = 2; i < n; i++){         dp[i] = Math.min(dp[i-2] , dp[i-1]) + cost[i]     }     return dp[n-1] }
  复杂度分析:  时间复杂度:O(n)  空间复杂度:O(n)
  优化:  let minCostClimbingStairs = function(cost) {     let n = cost.length,         n1 = cost[0],         n2 = cost[1]      for(let i = 2;i < n;i++){         let tmp = n2         n2 = Math.min(n1,n2)+cost[i]         n1 = tmp     }     return Math.min(n1,n2) }; 时间复杂度:O(n)  空间复杂度:O(1)
  更多解答
  7.3 最大子序和
  给定一个整数数组  nums   ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
  示例:  输入: [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
  进阶:
  如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
  第一步:定义子问题
  动态规划是将整个数组归纳考虑,假设我们已经知道了以第  i-1   个数结尾的连续子数组的最大和 dp[i-1]  ,显然以第i  个数结尾的连续子数组的最大和的可能取值要么为 dp[i-1]+nums[i]  ,要么就是 nums[i]   单独成一组,也就是 nums[i]   ,在这两个数中我们取最大值
  第二步:实现需要反复执行解决的子子问题部分  dp[n] = Math.max(dp[n−1]+nums[n], nums[n])
  第三步:识别并求解出边界条件  dp[0]=nums[0]
  最后一步:把尾码翻译成代码,处理一些边界情况
  因为我们在计算  dp[i]   的时候,只关心 dp[i-1]   与 nums[i]  ,因此不用把整个 dp   数组保存下来,只需设置一个 pre   保存 dp[i-1]   就好了。
  代码实现(优化):  let maxSubArray = function(nums) {     let max = nums[0], pre = 0     for(const num of nums) {         if(pre > 0) {             pre += num         } else {             pre = num         }         max = Math.max(max, pre)     }     return max }
  复杂度分析:  时间复杂度:O(n)  空间复杂度:O(1)
  7.4 买卖股票的最佳时机
  给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
  如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
  注意:你不能在买入股票前卖出股票。
  示例 1:  输入: [7,1,5,3,6,4] 输出: 5 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。      注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
  示例 2:  输入: [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
  解法:动态规划
  第一步:定义子问题
  动态规划是将整个数组归纳考虑,假设我们已经知道了  i-1   个股票的最大利润为 dp[i-1]  ,显然 i   个连续股票的最大利润为 dp[i-1]   ,要么就是就是 prices[i] - minprice   ( minprice   为前 i-1   支股票的最小值 ),在这两个数中我们取最大值
  第二步:实现需要反复执行解决的子子问题部分  dp[i] = Math.max(dp[i−1], prices[i] - minprice)
  第三步:识别并求解出边界条件  dp[0]=0
  最后一步:把尾码翻译成代码,处理一些边界情况
  因为我们在计算  dp[i]   的时候,只关心 dp[i-1]   与 prices[i]  ,因此不用把整个 dp   数组保存下来,只需设置一个 max   保存 dp[i-1]   就好了。
  代码实现(优化):  let maxProfit = function(prices) {     let max = 0, minprice = prices[0]     for(let i = 1; i < prices.length; i++) {         minprice = Math.min(prices[i], minprice)         max = Math.max(max, prices[i] - minprice)     }     return max }
  复杂度分析:  时间复杂度:O(n)  空间复杂度:O(1)
  7.5 回文子串
  给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
  具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
  示例 1:  输入:"abc" 输出:3 解释:三个回文子串: "a", "b", "c"
  示例 2:  输入:"aaa" 输出:6 解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
  提示:  输入的字符串长度不会超过 1000 。
  解法一:暴力法let countSubstrings = function(s) {   let count = 0   for (let i = 0; i < s.length; i++) {     for (let j = i; j < s.length; j++) {       if (isPalindrome(s.substring(i, j + 1))) {         count++       }     }   }   return count }  let isPalindrome = function(s) {   let i = 0, j = s.length - 1   while (i < j) {     if (s[i] != s[j]) return false     i++     j--   }   return true }
  复杂度分析:  时间复杂度:O(n^3^)  空间复杂度:O(1)
  解法二:动态规划
  一个字符串是回文串,它的首尾字符相同,且剩余子串也是一个回文串。其中,剩余子串是否为回文串,就是规模小一点的子问题,它的结果影响大问题的结果。
  我们怎么去描述子问题呢?
  显然,一个子串由两端的  i   、j   指针确定,就是描述子问题的变量,子串 s[i...j]   ( dp[i][j]   ) 是否是回文串,就是子问题。
  我们用二维数组记录计算过的子问题的结果,从base case出发,像填表一样递推出每个子问题的解。      j     a  a  b  a i a ✅   a    ✅     b       ✅   a          ✅
  注意:  i<=j   ,只需用半张表,竖向扫描
  所以:  i === j:dp[i][j]=true j - i == 1 && s[i] == s[j]:dp[i][j] = true j - i > 1 && s[i] == s[j] && dp[i + 1][j - 1]:dp[i][j] = true
  即:  s[i] == s[j] && (j - i <= 1 || dp[i + 1][j - 1]): dp[i][j]=true
  否则为  false
  代码实现:  let countSubstrings = function(s) {   const len = s.length   let count = 0   const dp = new Array(len)    for (let i = 0; i < len; i++) {     dp[i] = new Array(len).fill(false)   }   for (let j = 0; j < len; j++) {     for (let i = 0; i <= j; i++) {       if (s[i] == s[j] && (j - i <= 1 || dp[i + 1][j - 1])) {         dp[i][j] = true         count++       } else {         dp[i][j] = false       }     }   }   return count }
  代码实现(优化):
  把上图的表格竖向一列看作一维数组,还是竖向扫描,此时仅仅需要将  dp   定义为一维数组即可 let countSubstrings = function(s) {   const len = s.length   let count = 0   const dp = new Array(len)    for (let j = 0; j < len; j++) {     for (let i = 0; i <= j; i++) {       if (s[i] === s[j] && (j - i <= 1 || dp[i + 1])) {         dp[i] = true         count++       } else {         dp[i] = false       }     }   }   return count; }
  复杂度分析:  时间复杂度:O(n^2^)  空间复杂度:O(n)
  更多解答
  7.6 最长回文子串
  给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
  示例 1:  输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
  示例 2:  输入: "cbbd" 输出: "bb"
  解法:动态规划
  第 1 步:定义状态
  dp[i][j]   表示子串 s[i..j]   是否为回文子串,这里子串 s[i..j]   定义为左闭右闭区间,可以取到 s[i]   和 s[j]   。
  第 2 步:思考状态转移方程
  对于一个子串而言,如果它是回文串,那么在它的首尾增加一个相同字符,它仍然是个回文串  dp[i][j] = (s[i] === s[j]) && dp[i+1][j-1]
  第 3 步:初始状态 : dp[i][i] = true // 单个字符是回文串 if(s[i] === s[i+1]) dp[i][i+1] = true // 连续两个相同字符是回文串
  代码实现:  const longestPalindrome = (s) => {   if (s.length < 2) return s   // res: 最长回文子串   let res = s[0], dp = []   for (let i = 0; i < s.length; i++) {     dp[i][i] = true   }   for (let j = 1; j < s.length; j++) {     for (let i = 0; i < j; i++) {       if (j - i === 1 && s[i] === s[j]) {         dp[i][j] = true       } else if (s[i] === s[j] && dp[i + 1][j - 1]) {         dp[i][j] = true       }          // 获取当前最长回文子串       if (dp[i][j] && j - i + 1 > res.length) {         res = s.substring(i, j + 1)       }     }   }    return res }
  复杂度分析:  时间复杂度:O(n^2^)  空间复杂度:O(n^2^)
  7.7 最小路径和
  给定一个包含非负整数的  m x n   网格 grid   ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
  说明:每次只能向下或者向右移动一步。
  示例 1:  输入:grid = [[1,3,1],[1,5,1],[4,2,1]] 输出:7 解释:因为路径 1→3→1→1→1 的总和最小。
  示例 2:  输入:grid = [[1,2,3],[4,5,6]] 输出:12
  提示:  m == grid.length   n == grid[i].length   1 <= m, n <= 200   0 <= grid[i][j] <= 100
  1、DP方程  当前项最小路径和 = 当前项值 + 上项或左项中的最小值 grid[i][j] += Math.min( grid[i - 1][j], grid[i][j - 1] )
  2、边界处理  grid的第一行与第一列 分别没有上项与左项 故单独处理计算起项最小路径和 计算第一行: for(let j = 1; j < col; j++) grid[0][j] += grid[0][j - 1]
  计算第一列:  for(let i = 1; i < row; i++) grid[i][0] += grid[i - 1][0]
  3、代码实现  var minPathSum = function(grid) {     let row = grid.length, col = grid[0].length      // calc boundary     for(let i = 1; i < row; i++)         // calc first col         grid[i][0] += grid[i - 1][0]      for(let j = 1; j < col; j++)         // calc first row         grid[0][j] += grid[0][j - 1]      for(let i = 1; i < row; i++)         for(let j = 1; j < col; j++)             grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1])          return grid[row - 1][col - 1] };
  7.8 买卖股票的最佳时机 II
  给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
  设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
  注意:  你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  示例 1:  输入: [7,1,5,3,6,4] 输出: 7 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。      随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
  示例 2:  输入: [1,2,3,4,5] 输出: 4 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。      注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。      因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
  示例 3:  输入: [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
  提示:  1 <= prices.length <= 3 * 10 ^ 4   0 <= prices[i] <= 10 ^ 4
  解法一:峰底买入,峰顶卖出
  如图,在第二天买入,第三天卖出,第四天买入,第五天卖出获利最高,此处代码不再赘述,可以自己尝试写一下
  解法二:贪心算法
  贪心算法,故名思义,总是做出当前的最优选择,即期望通过局部的最优选择获得整体的最优选择。
  某种意义上说,贪心算法是很贪婪、很目光短浅的,它不从整体考虑,仅仅只关注当前的最大利益,所以说它做出的选择仅仅是某种意义上的局部最优,但是贪心算法在很多问题上还是能够拿到最优解或较优解,所以它的存在还是有意义的。
  对应于该题,第一天买入,第二天卖出,…,第  i   天买入,第 i+1   天卖出,如果 i   天买入第 i+1   天卖出有利润则买入,否则不买
  第  i-1   天买入第 i   天卖出获利 prices[i+1]-prices[i]   ,我们仅仅需要将 prices[i+1]-prices[i]   的所有正值加起来就是可获取的最大利益
  代码实现:  let maxProfit = function(prices) {     let profit = 0     for (let i = 0; i < prices.length - 1; i++) {         if (prices[i + 1] > prices[i]) {             profit += prices[i + 1] - prices[i]         }     }     return profit }
  复杂度分析:  时间复杂度:O(n)  空间复杂度:O(1)
  7.9 分发饼干
  假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 g~i~ ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 s~j~。如果 s~j~ >= g~i~ ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
  注意:
  你可以假设胃口值为正。一个小朋友最多只能拥有一块饼干。
  示例 1:  输入: [1,2,3], [1,1]  输出: 1  解释:  你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。
  示例 2:  输入: [1,2], [1,2,3]  输出: 2  解释:  你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 你拥有的饼干数量和尺寸都足以让所有孩子满足。 所以你应该输出2. 解法:贪心算法const findContentChildren = (g, s) => {     if (!g.length || !s.length) return 0          g.sort((a, b) => a - b)     s.sort((a, b) => a - b)          let gi = 0, si = 0     while (gi < g.length && si < s.length) {         if (g[gi] <= s[si++]) gi++     }     return gi }
  7.10 分割数组为连续子序列
  给你一个按升序排序的整数数组  num  (可能包含重复数字),请你将它们分割成一个或多个子序列,其中每个子序列都由连续整数组成且长度至少为 3 。
  如果可以完成上述分割,则返回  true   ;否则,返回 false   。
  示例 1:  输入: [1,2,3,3,4,5] 输出: True 解释: 你可以分割出这样两个连续子序列 :  1, 2, 3 3, 4, 5
  示例 2:  输入: [1,2,3,3,4,4,5,5] 输出: True 解释: 你可以分割出这样两个连续子序列 :  1, 2, 3, 4, 5 3, 4, 5
  示例 3:  输入: [1,2,3,4,4,5] 输出: False
  提示:  输入的数组长度范围为 [1, 10000]
  解法:贪心算法
  从头开始,我们每次仅仅寻找满足条件的序列(连续子序列长度为3),剔除之后,依次往后遍历:  判断当前元素是否能够拼接到前一个满足条件的连续子序列上,可以的话,则拼接  如果不可以,则判断以当前元素开始能否构成连续子序列(长度为3),可以的话,则剔除连续子序列  否则,返回 false  const isPossible = function(nums) {     let max = nums[nums.length - 1]     // arr:存储原数组中数字每个数字出现的次数     // tail:存储以数字num结尾的且符合题意的连续子序列个数     let arr = new Array(max + 2).fill(0),          tail = new Array(max + 2).fill(0)     for(let num of nums) {         arr[num] ++     }     for(let num of nums) {         if(arr[num] === 0) continue         else if(tail[num-1] > 0){             tail[num-1]--             tail[num]++         }else if(arr[num+1] > 0 && arr[num+2] > 0){             arr[num+1]--             arr[num+2]--             tail[num+2]++         } else {             return false         }         arr[num]--     }     return true }
  复杂度分析:  时间复杂度:O(n)  空间复杂度:O(n)
  7.11 全排列问题
  给定一个  没有重复  数字的序列,返回其所有可能的全排列。
  示例:  输入: [1,2,3] 输出: [   [1,2,3],   [1,3,2],   [2,1,3],   [2,3,1],   [3,1,2],   [3,2,1] ]
  解法:回溯算法
  本题是回溯算法的经典应用场景
  1. 算法策略
  回溯算法是一种搜索法,试探法,它会在每一步做出选择,一旦发现这个选择无法得到期望结果,就回溯回去,重新做出选择。深度优先搜索利用的就是回溯算法思想。
  2. 适用场景
  回溯算法很简单,它就是不断的尝试,直到拿到解。它的这种算法思想,使它通常用于解决广度的搜索问题,即从一组可能的解中,选择一个满足要求的解。
  3. 代码实现
  我们可以写一下,数组 [1, 2, 3] 的全排列有:  先写以 1 开头的全排列,它们是:[1, 2, 3], [1, 3, 2],即 1 + [2, 3] 的全排列;  再写以 2 开头的全排列,它们是:[2, 1, 3], [2, 3, 1],即 2 + [1, 3] 的全排列;  最后写以 3 开头的全排列,它们是:[3, 1, 2], [3, 2, 1],即 3 + [1, 2] 的全排列。
  即回溯的处理思想,有点类似枚举搜索。我们枚举所有的解,找到满足期望的解。为了有规律地枚举所有可能的解,避免遗漏和重复,我们把问题求解的过程分为多个阶段。每个阶段,我们都会面对一个岔路口,我们先随意选一条路走,当发现这条路走不通的时候(不符合期望的解),就回退到上一个岔路口,另选一种走法继续走。
  这显然是一个  递归  结构; 递归的终止条件是:一个排列中的数字已经选够了 ,因此我们需要一个变量来表示当前程序递归到第几层,我们把这个变量叫做  depth   ,或者命名为 index   ,表示当前要确定的是某个全排列中下标为 index   的那个数是多少; used(object):用于把表示一个数是否被选中,如果这个数字(num)被选择这设置为  used[num] = true   ,这样在考虑下一个位置的时候,就能够以 O(1)的时间复杂度判断这个数是否被选择过,这是一种「以空间换时间」的思想。 let permute = function(nums) {     // 使用一个数组保存所有可能的全排列     let res = []     if (nums.length === 0) {         return res     }     let used = {}, path = []     dfs(nums, nums.length, 0, path, used, res)     return res } let dfs = function(nums, len, depth, path, used, res) {     // 所有数都填完了     if (depth === len) {         res.push([...path])         return     }     for (let i = 0; i < len; i++) {         if (!used[i]) {             // 动态维护数组             path.push(nums[i])             used[i] = true             // 继续递归填下一个数             dfs(nums, len, depth + 1, path, used, res)             // 撤销操作             used[i] = false             path.pop()         }            } }
  4. 复杂度分析  时间复杂度:O(n∗n!),其中 n 为序列的长度这是一个排列组合,每层的排列组合数为:A^m^ ~n~=n!/(n−m)! ,故而所有的排列有 :A^1^ ~n~ + A^2^ ~n~ + … + A^n-1^ ~n~ = n!/(n−1)! + n!/(n−2)! + … + n! = n! * (1/(n−1)! + 1/(n−2)! + … + 1) <= n! * (1 + 1/2 + 1/4 + … + 1/2^n-1^) < 2 * n!并且每个内部结点循环 n 次,故非叶子结点的时间复杂度为 O(n∗n!)  空间复杂度:O(n)
  7.12 括号生成
  数字  n   代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的  括号组合。
  示例:  输入:n = 3 输出:[        "((()))",        "(()())",        "(())()",        "()(())",        "()()()"      ]
  解答:回溯算法(深度优先遍历)
  算法策略:  回溯算法是一种搜索法,试探法,它会在每一步做出选择,一旦发现这个选择无法得到期望结果,就回溯回去,重新做出选择。深度优先搜索利用的就是回溯算法思想。
  对应于本题,我们可以每次试探增加  (   或 )   ,注意: 加入  (   的条件是,当前是否还有 (   可以选择 加入  )   的时候,受到 (   的限制,如果已选择的结果里的 (   小于等于已选择里的 )   时,此时是不能选择 )   的,例如如果当前是 ()   ,继续选择 )   就是 ())   ,是不合法的
  代码实现:  const generateParenthesis = (n) => {     const res = []     const dfs = (path, left, right) => {         // 肯定不合法,提前结束         if (left > n || left < right) return         // 到达结束条件         if (left + right === 2 * n) {             res.push(path)             return         }         // 选择         dfs(path + "(", left + 1, right)         dfs(path + ")", left, right + 1)     }     dfs("", 0, 0)     return res }
  复杂度分析(来源leetcode官方题解):
  本文转载自三分钟学前端

CBA孙悦妻子海上度假!穿超短裙,开豪车与新男友游玩,离婚实锤目前CBA即将迎来第二阶段的比赛,目前各支球队已经开始了集训工作,而第二阶段的比赛时间已经确定,那就是12月1日到1月18日,第二阶段的比赛结束后正好是我们的春节,CBA球队也迎来干燥紧绷退退退!水光焕肤一夜回春有一个问题想问问大家,你们能保证自己的皮肤天天又润又健康吗?讲真,很多人都做不到,即使是天生丽质的人,也会因为天气突然干燥,或者突然熬了一个大夜,导致皮肤变干变差。好皮肤都受不得干郑明明山茶花精颜套组保湿抗氧化怎么样?95后现在购买护肤品品类最多的是抗初老产品,最近抗初老很受消费者关注,2530岁这个阶段是抗初老最佳的时间,可以减缓岁月在脸上留下的痕迹。今天分享一款宝藏抗初老护肤产品,郑明明山茶头脑的东西一切都是头脑的东西,我们该如何找回自己最近,五十多岁的安福路小公主,可以说是火出了天际。不算苗条的身材,却穿着可爱的洛丽塔公主裙,板寸头却配着闪亮的皇冠发箍。在我们身边,估计年轻的小姑娘,都很少有这样的穿着打扮。这位安一针灵之手麻痛妙用后溪透劳宫穴义小肠经气血由此上行督脉。名解1)后溪。后,与前相对,指穴内气血运行的人体部位为后背督脉之部。溪,穴内气血流行的道路。后溪名意指穴内气血外行于腰背的督脉之部。本穴物质为前谷穴传来肠道推粪机找到了,香蕉不在列,隔三差五吃一次,告别大肚腩随着生活水平的提高,很多人饮食习惯不规律,经常吃一些辛辣刺激的食物,再加上不爱运动,久而久之出现便秘问题,影响我们的健康。便秘问题真的是难受又难言,找对方法很重要。首选要食疗来改善富含抗炎抗衰的3脂肪酸的超级种子有多好(内附食谱)脂肪分为不同类型反式脂肪酸饱和脂肪酸单不饱和脂肪酸多不饱和脂肪酸。3脂肪酸和6脂肪酸就属于多不饱和脂肪酸,这二者属于必需品。因为我们的身体不具备制造它们的能力,所以得从饮食中获取。娃身上长胎记,一查竟是瘤关于胎记民间流传着各种各样的说法其中一个是不同部位的胎记代表着不同的命运比如屁股下方的胎记象征着富贵命所以有些老一辈的爷爷奶奶是不允许爸妈处理掉宝宝身上的胎记的但现在已经是2022吃水果的好处和坏处是什么呢吃水果的优点大多是可以提供一些营养物质,以及水分,同样也可以起到促进肠胃蠕动的作用,但是也有一定的坏处,比如在吃了一些热带水果之后,就会引起上火。1吃水果的好处水果的种类很多,如果食管癌有哪些表现呢?食管癌是我国发病率较高的严重疾病,所以除了做好日常生活中的预防工作外,我们还应该全面了解这种疾病,以便更好地远离或尽快发现这种疾病。接下来,专家将介绍食管癌的表现。食道癌有哪些症状菟丝子补肾益精,搭配四个药,还调失眠补肝肾滋肾阳健脾胃大家好,我是李医生我们知道,在中医理论中,人活着,离不开一个精字,而精它分为先天之精和后天之精,先天之精它主要是来源于父母主生殖的精气,而后天之精则源于水谷精微的化生,它们由此构成
突发!拜登宣布紧急状态!联合国最新发声,美国拒绝!美韩军演突生波折,发生了什么?这个周末,突发消息不断。美国的冬季风暴愈发凶险。当地时间3月10日,根据美国白宫发布的一份声明,美国总统拜登当日宣布,加利福尼亚州进入紧急状态,以应对该地区的严重冬季风暴洪水山体滑IPO期间遭取消专精特新认定德和科技能否突出重围?21世纪经济报道记者张梓桐实习生柴雨馨上海报道2月27日,德和科技发布招股说明书。招股书显示,在IPO闯关关键期,德和科技遭到群众举报,其专精特新小巨人荣誉因此被工信部取消,荣誉带四川银行发挥金融力量推出以工代赈项目综合服务方案中证网讯(记者康曦)3月10日,四川省眉山市东坡区举行2023年以工代赈项目集中开工仪式,开工仪式上四川银行推出了以工代赈项目综合服务方案,为以工代赈注入金融血液。四川省眉山市东坡两会即将结束,又有三个良心的提议,都是深得民心两会是一个关乎到老百姓幸福生活的重大会议,全民都共同关注。来自全国各地的代表,政协委员聚集在一起,为老百姓提出自己的建议,如果便民,利民的好建议通过,将是对老百姓的生活是一大福利,两会将要结束,5个议案和提案中,我认为有2个被采纳的可能性较大两会已进行几天,政协会议今天已经闭幕,人大会议将于3月13日结束,它们承载着大多人的期盼与希望春天蕴含着勃勃生机,催促着新希望的前进脚步。全国两会承载着中国人民对美好生活的新期待,冤枉坐16年牢,精神损失费只赔68万?不服!最高法院赔120万近日,最高人民法院对一起冤假错案进行了再审,并判决原告王某某获得120万元的精神损害抚慰金。这是继呼格吉勒图案和聂树斌案之后,又一起精神损害赔偿金额超过百万元的案件。据悉,王某某于从没见过如此狂妄的弱敌,有65的韩国人支持武力干涉中国统一文小明最近,韩国公布了2022年底的一份民意调查表让国人震惊,调查问题是在中国大陆武力统一台岛时,韩国应该怎么做?结果显示64。5的韩国民众认为韩国军队应跟随美军介入台海冲突,22张颂文又一大剧定了,好家伙,又是神仙阵容,导演是潜伏编剧徐江曾对高启强说过天生干特务的,选这么个地方。啊,这不,大哥立马在另外一部剧里演起了苏州特工站站长周知非,不日即全组杀青。看这极有氛围感的剧照,只见张颂文身穿黑色中山装行走于风雪中范明娶了大他6岁二婚女总裁,对继子视如己出,今继子成他骄傲阅读此文之前,麻烦您点击下关注,方便与您讨论分享,也能及时观看下一篇精彩文章。说起范明可能大部分观众都不知道这是谁但是说起武林外传里的邢捕头,观众就比较清楚了。邢捕头的扮演者名字就十年来第一次杀进亚青赛八强,中国足球有希望了吗?近十年来的最佳战绩,为目前低迷的中国足球带来了新希望之光尽管这是黑暗中的微光。1亚青赛(U20亚洲杯)小组赛最后一轮比赛,中国U20对阵吉尔吉斯斯坦U20。由于国青队此前1胜1负积中国排球全力出击2022年女排世锦赛,中国女排庆祝胜利。人民视觉世界男排联赛,张景胤强攻。人民视觉广州日报全媒体记者杨敏2024年巴黎奥运会排球项目资格赛将在今年9月10月之间展开。女排奥运资格赛