JS算法之骰子点数和扑克牌顺子

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

n个骰子的点数

剑指Offer 60. n个骰子的点数

难度:中等

题目:leetcode-cn.com/problems/ng…

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

你需要用一个浮点数数组返回答案,其中第i个元素代表这n个骰子所能掷出的点数集合中第i小的那个概率。

示例1:

 输入: 1
 输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
复制代码

示例2:

 输入: 2
 输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]
复制代码

限制:1 <= n <= 11

题解

n个骰子,如果采用暴力法,那么时间复杂度将会达到O(​),显然不切实际。

动态规划

由于后面的概率或者点数和与前面骰子的概率和点数和有关系,我们可以使用动态规划的方式解决这道题。

步骤如下:

  • 设置dp为每轮掷骰子的点数和的概率,初始为点数和为1-6的概率均为​。

  • 根据掷骰子的次数,来进行循环

    • 设置tmp为该轮掷骰子的点数和的概率,初始为全0。若掷5颗骰子,5个全1的情况下,能得到最小的点数和为5,同理,不难得出最小点数和为6n-n+1 = 5n+1。
    • 根据上一次的dp结果,与每轮掷骰子的tmp[i]一起交替前进,因为新增骰子的点数只可能是1至6,因此每一次tmp前进时都要加上「上一次的结果/6」。
    • 算完tmp后将tmp赋值给dp,以便下一次可以使用。
  • 遍历结束,返回dp。

 /**
  * @param {number} n
  * @return {number[]}
  */
 var dicesProbability = function (n) {
   let dp = new Array(6).fill(1/6);
   for (let i = 2; i <= n; i++) {
     let tmp = new Array(5 * i + 1).fill(0);
     for (let j = 0; j < dp.length; j++) {
       for (let k = 0; k < 6; k++) {
         tmp[j + k] += dp[j] / 6;
       }
     }
     dp = tmp;
   }
   return dp;
 };
复制代码
  • 时间复杂度:O(N^2)
  • 空间复杂度:O(N)

扑克牌的顺子

剑指Offer 61. 扑克牌中的顺子

难度:简单

题目:leetcode-cn.com/problems/bu…

从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王都为0,可以看成任意数字。A不能视为14。

示例1:

 输入: [1,2,3,4,5]
 输出: True
复制代码

示例2:

 输入: [0,0,1,2,5]
 输出: True
复制代码

限制:数组长度为5,数组的数取值为[0,13]。

题解

一开始看到示例2很疑惑,为啥[0,0,1,2,5]是一个顺子,其实就是说0是一张万能牌,用0可以充当任何一张牌,就可以将其看成[1,2,3,4,5]啦。

判断5张牌是顺子的条件如下:

  • 除了大小王外,其他所有牌无重复
  • 5张牌中需满足「最大的牌 - 最小的牌(大小王除外) < 5」

法一 集合 + 遍历

  • 使用Set去重
  • 遍历的时候获取最大牌max和最小牌min。
 /**
  * @param {number[]} nums
  * @return {boolean}
  */
 var isStraight = function (nums) {
   const set = new Set();
   let max = 0,
     min = 14;
   for (let num of nums) {
     if (num === 0) continue;
     max = Math.max(max, num);
     min = Math.min(min, num);
     if (set.has(num)) return false;
     set.add(num);
   }
   return max - min < 5;
 };
复制代码
  • 时间复杂度:O(N),本题中N为5
  • 空间复杂度:O(N)

法二 排序 + 遍历

  • 先对数组排序
  • 判断nums[i]是否与nums[i+1]相等(判断重复),相等则直接返回false
  • 排序后,末尾元素nums[4]为最大牌,元素nums[joker]为最小牌,其中joker为大小王的数量
 var isStraight = function (nums) {
   let joker = 0; // 用于判断大小王的数量,从而作为下标
   nums.sort((a, b) => a - b); // 记得加上sort()内的函数
   // for循环只需要判断前4位即可
   for (let i = 0; i < 4; i++) {
     if (nums[i] === 0) joker++;
     else if (nums[i] === nums[i + 1]) return false;
   }
   return nums[4] - nums[joker] < 5;
 };
复制代码
  • 时间复杂度:O(NlogN),sort使用的是快排需要O(NlogN)
  • 空间复杂度:O(1)

坚持每日一练!前端小萌新一枚,希望能点个哇~

猜你喜欢

转载自juejin.im/post/6995931634151194637
今日推荐