Leetcode刷题171-190

①338.比特位计数

2024.4.29 周一
题目:
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,
返回一个长度为 n + 1 的数组 ans 作为答案。
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 –> 0
1 –> 1
2 –> 10

思路1:
直接使用位运算算出每个数的含有1的位数
或者使用Integer.bitCount(n)计算
41% self 暴力求解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int[] countBits(int n) {
int[] res = new int[n + 1];

for(int i = 0; i <= n; i++){
res[i] = oneCount(i);
}
return res;
}

//计算某二进制数的1的个数
//相当于Integer.bitCount(n);
public int oneCount(int n){
int count = 0;
while(n > 0){
count += n & 1;
n >>= 1;
}
return count;
}
}

思路2:
奇数:每个奇数都比前一个偶数多1个1的个数,
如果已经知道前一个偶数的个数了,后一个奇数就不用算了,直接加一即可

0 –> 0
1 –> 1

2 –> 10
3 –> 11

4 –> 100
5 –> 101

偶数:二进制表示中,偶数中 1 的个数一定和乘 2 之后的那个数一样多,
相当于向左移一位

2 = 10 4 = 100 8 = 1000
3 = 11 6 = 110 12 = 1100

此外:0 的 1 个数为 0,可以根据奇偶性开始遍历计算了

奇数则前一个偶数+1,偶数直接等于折半数

99%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int[] countBits(int n) {
int[] res = new int[n + 1];
res[0] = 0;

for(int i = 1; i <= n; i++){
if(i % 2 == 1){
//奇数
res[i] = res[i-1] + 1;
}else{
//偶数
res[i] = res[i/2];
}
}
return res;
}
}

思路3:
如果 x 是偶数,则 bits[x]=bits[⌊x/2⌋]
如果 x 是奇数,则 bits[x]=bits[⌊x/2⌋] + 1
上述两种情况可以合并成:bits[x] 的值等于 bits[⌊x/2⌋] 的值加上 x 除以 2 的余数。
⌊x/2⌋ 可以通过 x >> 1得到,x除以2的余数可以通过 x & 1得到
因此:
result[i] = result[i >> 1] + (i & 1);

⑧LCR 003.比特位计数

2024.5.9 周四
题目:
给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。

33% self Integer.bitCount(i)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int[] countBits(int n) {
int[] res = new int[n + 1];
for(int i = 0; i <= n; i++){
res[i] = Integer.bitCount(i);
}
return res;
}
}

//Integer.bitCount(i); ↓
public int bitCount(int n){
int count = 0;
while(n > 0){
count += n & 1;
n >>= 1;
}
return count;
}

②392.判断子序列

2024.4.30 周二
题目:
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。
(例如,”ace”是”abcde”的一个子序列,而”aec”不是)
进阶:
如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。
在这种情况下,你会怎样改变代码?

思路1:
暴力 双指针 90% self

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public boolean isSubsequence(String s, String t) {
int first = 0;
int second = 0;

while(first < s.length() && second < t.length()){
if(s.charAt(first) == t.charAt(second)){
first++;
}
second++;
}
return first == s.length();
}
}

思路2:
使用双指针的话,时间大量用于在 t 中找到下一个匹配字符。
预处理:
对于 t 的每一个位置,从该位置开始往后的每一个字符第一次出现的位置;
f(i)(j)表示从位置 i 开始往后,字符 j 第一次出现的位置;

1.若 t 的位置 i 的字符就是 j:
f(i)(j) = i
2.否则, j 出现在位置 i+1 开始往后:
f(i)(j) = f(i+1)(j)
倒过来进行动态规划,从后往前枚举 i (知道后一个就能知道前一个)

int m = t.length()

边界状态:
f(m-1)(..) = m 让其正常进行转移
如果:
f(i)(j) = m 则表示从位置 i 开始往后不存在字符 j

该解法中对 t 的处理与 s 无关,且预处理完成后,
可以利用预处理数组的信息,线性地算出任意一个字符串 s 是否为 t 的子串。

动态规划 15%

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
30
31
32
33
class Solution {
public boolean isSubsequence(String s, String t) {
int n = s.length();
int m = t.length();

// 0 1 2 ... m-1 m
int[][] f = new int[m+1][26];
for(int i = 0; i < 26; i++){
f[m][i] = m; //从位置 m 开始往后不存在字符 i
}

//从后往前
for(int i = m-1; i >= 0; i--){
for(int j = 0; j < 26; j++){
if(t.charAt(i) == j + 'a'){
f[i][j] = i;
}else{
f[i][j] = f[i+1][j];
}
}
}
//记录当前匹配到的位置
int add = 0;
//查找
for(int i = 0; i < n; i++){
if(f[add][s.charAt(i) - 'a'] == m){
return false;
}
add = f[add][s.charAt(i) - 'a'] + 1;
}
return true;
}
}

③509.斐波那契数

2024.5.1 周三
题目:
斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。
该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。
也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。

思路1:
使用一个数组记录每一个F(0) - F(n)

0 1 1 2 3 5
0 1 2 3 4 5

100% self

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public int fib(int n) {
if(n <= 1) return n;
int[] temp = new int[n + 1];
temp[0] = 0;
temp[1] = 1;

for(int i = 2; i < n+1; i++){
temp[i] = temp[i-1] + temp[i-2];
}
return temp[n];
}
}

思路2:
滚动数组,不保留过程,直接得到结果(动态规划)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int fib(int n) {
if(n <= 1) return n;

int p = 0;
int q = 0;
int r = 1;
for(int i = 2; i <= n; i++){
p = q;
q = r;
r = p + q;
}
return r;
}
}

⑩LCR126.斐波那契数

2024.5.11 周刘(66666666)
题目:
斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。
该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。
也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
答案需要取模 1e9+7(1000000007) ,如计算初始结果为:1000000008,请返回 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int fib(int n) {
final int mod = 1000000007;

if(n < 2) return n;

int[] f = new int[n+1];

f[0] = 0;
f[1] = 1;

for(int i = 2; i <= n; i++){
int temp = (f[i-1] + f[i-2]) % mod;
f[i] = temp;
}
return f[n];

}
}

⑤1137.第N个泰波那契数

2024.5.6 周一
题目:
泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

self 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int tribonacci(int n) {
int[] temp = new int[n + 3];

temp[0] = 0;
temp[1] = 1;
temp[2] = 1;

if(n < 3) return temp[n];

for(int i = 3; i <= n; i++){
temp[i] = temp[i-1] + temp[i-2] + temp[i-3];
}
return temp[n];
}
}

动态规划 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int tribonacci(int n) {
if(n == 0) return 0;
if(n <= 2) return 1;

int p = 0;
int q = 0;
int r = 1;
int s = 1;
for(int i = 3; i <= n; i++){
p = q;
q = r;
r = s;
s = p + q + r;
}
return s;
}
}

④1025.除数博弈

2024.5.5 周日
(一维 dp)
题目:
爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局
最初,黑板上有一个数字 n 。在每个玩家的回合,玩家需要执行以下操作:

  • 选出任一 x,满足 0 < x < n 且 n % x == 0 。
  • 用 n - x 替换黑板上的数字 n 。
    如果玩家无法执行这些操作,就会输掉游戏。
    只有在爱丽丝在游戏中取得胜利时才返回 true 。
    假设两个玩家都以最佳状态参与游戏。

思考1:数学找规律的方法
n = 1 的时候,区间 (0,1) 中没有整数是 n 的因数
最后谁先拿到偶数 2 谁就赢了
爱丽丝先手开局
两个玩家都以最佳状态参与游戏

奇数的因子都是奇数
奇数 - 奇数 = 偶数
如果 n 是奇数,爱丽丝先拿到一个奇数,抛给鲍勃一个偶数,
如果只要鲍勃选择-1,还给爱丽丝一个奇数,最后一定是鲍勃先拿到偶数2,
爱丽丝必输。
如果 n 是偶数,爱丽丝只需要每次选择-1,爱丽丝必赢。

数学 100%

1
2
3
4
5
class Solution {
public boolean divisorGame(int n) {
return n % 2 == 0;
}
}

思考2:

n = k B = m
n = 1 A败
n = 2 A取1 .. B = 1 B败
n = 3 A取1 .. B = 2 A败
n = 4 A取1/2 若取1 .. B = 3 B败
是否存在一个必败态,爱丽丝直接执行对应的操作让当前的数字变成m,爱丽丝就赢了,如果没有任何一个是必败的状态的话,说明爱丽丝无论怎么进行操作,最后鲍勃处于必胜,爱丽丝必败。
f(i) 表示当前数字 i 的时候先手处于必胜还是必败态,从前往后递推,枚举 i 在(0,i)中 i 的因数 j ,看是否存在 f(i-j)为必败态即可

动态规划 33%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean divisorGame(int n) {
boolean[] f = new boolean[n + 3];

//最基本情况
f[1] = false;
f[2] = true;

for(int i = 3; i <= n; i++){
for(int j = 1; j < i; j++){
//只要做出的选择其中之一,能让对方输,在当前这一步我们就可以赢了
if(i % j == 0 && !f[i-j]){
f[i] = true;
break;
}
}
}
return f[n];
}
}

⑥1668最大重复子字符串

2024.5.7 周二
(一维 dp)
题目:
给你一个字符串 sequence ,如果字符串 word 连续重复 k 次形成的字符串是 sequence 的一个子字符串,那么单词 word 的 重复值为 k 。
单词 word 的 最大重复值 是单词 word 在 sequence 中最大的重复值。如果 word 不是 sequence 的子串,那么重复值 k 为 0 。
给你一个字符串 sequence 和 word ,请你返回 最大重复值 k 。

思考:
f[i] 为以 sequence[i] 结尾时的最大重复值
每次转移 f[i] 时从 sequence 中截取以 sequence[i] 为结尾,长度为 m 的后缀字符串
转移: f[i] = f[i-m] + 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int maxRepeating(String sequence, String word) {
int n = sequence.length();
int m = word.length();

if(n < m) return 0;

int res = 0;

int[] f = new int[n+1];
for(int i = 1; i <= n; i++){
if(i-m < 0) continue;
if(sequence.substring(i-m, i).equals(word)){
f[i] = f[i - m] + 1;
res = Math.max(res,f[i]);
}
}
return res;
}
}

⑦LCP07.传递信息

2024.5.8 周三
(二维 dp)
题目:
小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:
有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。
传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
每轮信息必须需要传递给另一个人,且信息可重复经过同一个人
给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。
返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的 方案数
若不能到达,返回 0。
2 <= n <= 10
1 <= k <= 5
示例 :
输入:n = 5, relation = [[0,2],[2,1],[3,4],[2,3],[1,4],[2,0],[0,4]], k = 3
输出:3
解释:信息从小 A 编号 0 处开始,经 3 轮传递,到达编号 4。
共有 3 种方案,分别是 0->2->0->4, 0->2->1->4, 0->2->3->4。

思路1:
动态规划

relation 传递情况
0->2
0->4

1->4
2->1

2->3
2->0
3->4

dp[0,0] = 1 dp[0,1] = 0 dp[0,2] = 0 dp[0,3] = 0 dp[0,4] = 0 //第 0 轮除了只能传递给 0 编号人这一种方案,其他方案不存在
dp[1,0] = 0 dp[1,1] = 0 dp[1,2] = 1 dp[1,3] = 0 dp[1,4] = 1 //举例第 1 轮中传递给 2 编号人的方案数,relation 中存在 0->2 一种情况,在第 0 轮存在传递给 0 编号的方案数为 1
dp[2,0] = 1 dp[2,1] = 1 dp[2,2] = 0 dp[2,3] = 1 dp[2,4] = 0 //举例 第 2 轮中传递给 4 编号人的方案数,relation 中存在 0->4、1->4、3->4 三种情况,但是在第 1 轮中传递给编号 0、1、3 人的方案数均为 0

dp(i)(j) 经过 i 轮,传递到编号 j 的玩家的方案数
0 ≤ i ≤ k
0 ≤ j < n

边界情况:
从编号 0 开始,第 0 轮一定位于编号 0 玩家,不可能传递给其他玩家
dp(0)(0) = 1
dp(0)(j) = 0 (j ≠ 0)

对于传递关系:(src,dst)

如果第 i 轮传递到编号 src 的玩家
则第 i + 1 轮从编号 src 传递到编号 dst 玩家
在计算 dp(i + 1)(dst) 时,需要考虑传递到 dst 的所有玩家

转移方程:
dp(i + 1)(dst) = dp(i)(src)求和
最后返回 f(k)(n-1)

动态规划 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int numWays(int n, int[][] relation, int k) {
int[][] dp = new int[k+1][n];
dp[0][0] = 1;

for(int i = 0; i < k; i++){
//在 relation 中寻找可能的情况
for(int[] temp : relation){
int src = temp[0];
int dst = temp[1];
dp[i+1][dst] += dp[i][src];
}
}
return dp[k][n-1];
}
}

改进 一维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int numWays(int n, int[][] relation, int k) {
int[] dp = new int[n];
dp[0] = 1;

for(int i = 0; i < k; i++){
int[] next = new int[n];
for(int[] edge : relation){
int src = edge[0];
int dst = edge[1];
next[dst] += dp[src];
}
dp = next;
}
return dp[n - 1];

}
}

思路2:
深度优先搜索
寻找从编号 0 的玩家经过 k 轮传递到编号 n−1 的玩家处的方案数
等价于在有向图中寻找从节点 0 到节点 n−1 的长度为 k 的路径数,同一条路径可以重复经过同一个节点。

使用 edges 列表存储有向边的关系,即存储每个玩家可以到达的下一个玩家的编号

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Solution {
int ways;//方案数
int n; //总人数
int k; //总步数
List<List<Integer>> edges; //relation中每个玩家可以传递给的下一个玩家的编号


public int numWays(int n, int[][] relation, int k) {
ways = 0;
this.n = n;
this.k = k;

//对 relation 传递关系进行预处理
edges = new ArrayList<List<Integer>>();
for(int i = 0; i < n; i++){
edges.add(new ArrayList<Integer>());
}
for(int[] edge : relation){
int src = edge[0];
int dst = edge[1];
edges.get(src).add(dst);
}
dfs(0,0);
return ways;
}


public void dfs(int index, int steps){
//到当前玩家所用的步数
if(steps == k){
if(index == n-1){
//满足条件,形成一个合适方案
ways++;
}
//步数到了但没有到达n-1玩家也要return 不满足条件
return;
}
//寻找当前玩家可以传递给的下一个玩家
List<Integer> list = edges.get(index);
for(int nextIndex : list){
dfs(nextIndex,steps + 1);
}
}
}

⑨LCR 088.使用最小花费爬楼梯

2024.5.10
sasy
题目:
数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。
每当爬上一个阶梯都要花费对应的体力值,一旦支付了相应的体力值,就可以选择向上爬一个阶梯或者爬两个阶梯。
请找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。

思考:
只要动就需要支付当前阶梯上的费用,
可以选择从cost[0]、cost[i]开始,
要超过 cost 的长度,(相当于要计算到达下标[n]时的最小花费)

f(i)到达阶梯 i 时的总花费
到达起始台阶不花钱
f[0] == 0
f[1] == 0

谁小用谁
一步:
cost[i-1] < cost[i-2]
f(i) = f(i-1) + cost(i-1);
两步:
cost[i-1] > cost[i-2]
f(i) = f(i-2) + cost(i-2);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int minCostClimbingStairs(int[] cost) {
int n = cost.length;
int[] dp = new int[n+1];

dp[0] = 0;
dp[1] = 0;

for(int i = 2; i <= n; i++){
dp[i] = Math.min(dp[i-1] + cost[i-1],dp[i-2] + cost[i-2]);
}
return dp[n];
}
}

当 i≥2i \ge 2i≥2 时,dp[i] 只和 dp[i−1] 与 dp[i−2] 有关,因此可以使用滚动数组的思想,将空间复杂度优化到 O(1)。

滚动数组 优化空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int minCostClimbingStairs(int[] cost) {
int n = cost.length;

int pre = 0;
int curr = 0;

for(int i = 2; i <= n; i++){
int next = Math.min(curr + cost[i-1],pre + cost[i-2]);
pre = curr;
curr = next;
}
return curr;
}
}

⑪LCR 127.跳跃训练

2024.5.12
题目:
今天的有氧运动训练内容是在一个长条形的平台上跳跃。
平台有 num 个小格子,每次可以选择跳 一个格子 或者 两个格子。
请返回在训练过程中,学员们共有多少种不同的跳跃方式。
结果可能过大,因此结果需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

思考:
即需要求,到达格子 num 时共有多少种跳跃方式
dp[i] = dp[i-1] + dp[i-2];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int trainWays(int num) {
int[] dp = new int[num];
int mod = 1000000007;

if(num == 0) return 1;

if(num < 3){
return num;
}

dp[0] = 1;
dp[1] = 2;

for(int i = 2; i < num; i++){
int temp = (dp[i-1] + dp[i-2]) % mod;
dp[i] = temp;
}

return dp[num-1];

}
}

⑩LCR126.斐波那契数

2024.5.11 周刘(66666666)
题目:
斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。
该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。
也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
答案需要取模 1e9+7(1000000007) ,如计算初始结果为:1000000008,请返回 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int fib(int n) {
final int mod = 1000000007;

if(n < 2) return n;

int[] f = new int[n+1];

f[0] = 0;
f[1] = 1;

for(int i = 2; i <= n; i++){
int temp = (f[i-1] + f[i-2]) % mod;
f[i] = temp;
}
return f[n];

}
}

⑫LCR161.连续天数的最高销售额

2024.5.14 周二
题目:
某公司每日销售额记于整数数组 sales ,请返回所有 连续 一或多天销售额总和的最大值。
要求实现时间复杂度为 O(n) 的算法。

思考:
要求连续、无连续多少的要求
可1天、也可连续多天

dp[i] 以第 i 个数为结尾的最大的销售额
dp(i) = max(dp(i-1) + sales[i] , salse[i])
初始:dp[0] = salses[0]

只记录最大值 100%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int maxSales(int[] sales) {
int n = sales.length;

int pre = 0; //只记录到目前为止最大的 sales
int maxSale = sales[0];

for(int sale : sales){
pre = Math.max(sale , pre + sale);
maxSale = Math.max(maxSale , pre);
}
return maxSale;
}
}

有数组记录 45%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int maxSales(int[] sales) {
//用一个数组全程记录
int n = sales.length;
int[] dp = new int[n];

dp[0] = sales[0];
int maxSale = sales[0];

for(int i = 1; i < n; i++){
dp[i] = Math.max(sales[i] , dp[i-1] + sales[i]);
maxSale = Math.max(maxSale , dp[i]);
}
return maxSale;
}
}

⑬LCS 01.下载插件

2024.5.15 周三
题目:
小扣打算给自己的 VS code 安装使用插件,初始状态下带宽每分钟可以完成 1 个插件的下载。
假定每分钟选择以下两种策略之一:
使用当前带宽下载插件
将带宽加倍(下载插件数量随之加倍)
请返回小扣完成下载 n 个插件最少需要多少分钟。
注意:实际的下载的插件数量可以超过 n 个

思考:
n = 2 res = 2
n = 4 res = 3

⑭面试题05.03翻转数位

题目:

⑮面试题08.01.三步问题

题目:

⑯面试题16.17.连续数列

题目:

⑰面试题17.16.按摩师

题目:

题目:

题目:

题目:

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2023-2025 Annie
  • Visitors: | Views:

嘿嘿 请我吃小蛋糕吧~

支付宝
微信