n个骰子的点数

剑指 Offer 60. n个骰子的点数 - 力扣(Leetcode)

题目要求

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

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

例子:

1
2
3
4
5
输入: 1
输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]

输入: 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

思路分析

动态对话

  1. dp函数的含义:当骰子的个数为i时,点数为j时,一共有dp[i][j]中组合;
  2. 关系式:dp[i][j] = dp[i-1][j-1] + dp[i-1][j-2]+...+dp[i-1][j-6];
  3. 初始值:dp[1][1] = 1;dp[1][2] = 1 ... dp[1][6] = 1;

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public double[] dicesProbability(int n) {
// dp数组 大小为n,6n
int[][] dp = new int[n+1][6*n+1];
// 初始化
for(int i = 1;i <= 6; i++){
dp[1][i] = 1;
}

for(int i=2;i<=n;i++){
for(int j=i;j<=6 * i;j++){
for(int k=1;k<=6;k++){
if(j < k) break;
dp[i][j] += dp[i-1][j-k];
}
}
}
// res存放返回结果
double[] res = new double[5 * n +1];
// 记录下标
int index = 0;
double sum = Math.pow(6,n); // 组合数

for(int i =n;i <= 6*n;i++){
res[index++] = dp[n][i] / sum;
}
return res;
}
}

扑克牌中的顺子

面试题61. 扑克牌中的顺子 - 力扣(Leetcode)

题目要求

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

例子:

1
2
3
4
5
输入: [1,2,3,4,5]
输出: True

输入: [0,0,1,2,5]
输出: True

提示:

  • 数组长度为 5

  • 数组的数取值为 [0, 13] .

思路分析

集合 时间O(n) 空间O(n)

若要成为顺子,需要满足以下条件:

  1. 不能存在重复的数,大小王除外;
  2. 最大值 - 最小值 < 5,大小王除外;

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public boolean isStraight(int[] nums) {
// 定义一个集合
Set<Integer> res = new HashSet<>();
// 定义最大值和最小值(初始赋值大一点就可以)
int max = -1,min = 25;
for(int i=0;i<nums.length;i++){
// 大小王
if(nums[i] == 0) continue;
// 如果nums[i]已经在集合内,说明出现重复值
if(res.contains(nums[i])) return false;
// 加入到集合
res.add(nums[i]);
// 统计最大值和最小值
max = Math.max(nums[i],max);
min = Math.min(nums[i],min);
}
// 最大值-最小值<5是否成立
return max - min < 5;
}
}

圆圈中最后剩下的数字

剑指 Offer 62. 圆圈中最后剩下的数字 - 力扣(Leetcode)

题目要求

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

例子:

1
2
3
4
5
输入: n = 5, m = 3
输出: 3

输入: n = 10, m = 17
输出: 2

提示:

  • 1 <= n <= 10^5
  • 1 <= m <= 10^6

思路分析

经典约瑟夫环问题,可以使用环形链表(时间O(n*n))解决,这里使用递归(时间O(n))来解决

  • 每次删除某一个数字后,就对剩余的这些数字重新编号,难点时找出删除前和删除后数字编号的映射关系;
  • 递归函数定义:f(n,m)表示当数字个数为m时,剩余数字的编号为f(n,m);
  • 难点:找出f(n,m)和f(n-1,m)的关系;
  • 通过分析删除前后n和m的关系,可以得出:new = (old +m)%n,即f(n,m) = (f(n-1,m)+m) % n;
  • 若m = 0,表示只剩最后一个数字,返回即可;

环形链表解决参考:【Java 数据结构与算法】单向环形链表 | Hey,Joker (jokerdig.com)

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int lastRemaining(int n, int m) {
// 特殊情况
if(n == 0) return n;
// 递归关系式:f(n,m) = (f(n-1,m)+m) % n;
// return (lastRemaining(n-1,m)+m) % n;
// 优化
int res = 1;
for(int i=1;i <= n;i++){
res = (res + m) % i;
}
return res;
}
}

股票的最大利润

剑指 Offer 63. 股票的最大利润 - 力扣(Leetcode)

题目要求

假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?

例子:

1
2
3
4
5
6
7
8
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0

提示:

0 <= 数组长度 <= 10^5

思路分析

  1. 定义int min = Integer.MAX_VALUE,max = 0;
  2. 再遍历的过程中,时刻将买入最小值保存到min;
  3. 当遇到当前值prices[i]大于min,就试着卖出,即prices[i]-min,并将结果保存到max,max来记录利润的最大值;
  4. 最后将max返回即可

代码实现

时间O(n) 空间O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int maxProfit(int[] prices) {
int min = Integer.MAX_VALUE;
int max = 0;

// 遍历
for(int i=0;i<prices.length;i++){
// 如果当前值比min小,就保存到min中
if(prices[i] < min){
min = prices[i];
}else{
// 试着卖出去
max = Math.max(max,prices[i]-min);
}
}
return max;
}
}

求1+2+…+n

剑指 Offer 64. 求1+2+…+n - 力扣(Leetcode)

题目要求

1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

例子:

1
2
3
4
5
输入: n = 3
输出: 6

输入: n = 9
输出: 45

提示:

1 <= n <= 10000

思路分析

使用递归来实现,但是不能使用题目说到的关键字,那我们就考虑使用与或门来实现递归的停止条件;

代码实现

1
2
3
4
5
6
7
8
9
10
class Solution {
int sum = 0; // 定义全局sum
public int sumNums(int n) {

// 使用递归,这里的falg,n >= 1和 sumNums > 1 只是用来辅助,让递归停止的
boolean falg = n >= 1 && sumNums(n - 1) > 1;
sum = sum + n; // 记录结果
return sum;
}
}