跳到主要内容

15 篇博文 含有标签「双指针」

查看所有标签

· 阅读需 3 分钟

1、题干

给你两个按 非递减顺序 排列的整数数组 nums1 nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。

请你 合并 nums2 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n

 

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

 

提示:

  • nums1.length == m + n
  • nums2.length == n
  • 0 <= m, n <= 200
  • 1 <= m + n <= 200
  • -109 <= nums1[i], nums2[j] <= 109

 

进阶:你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗?

2、思路

两种思路:双指针,合并后排序(无脑暴力)

3、代码

  • 双指针
function merge(nums1: number[], m: number, nums2: number[], n: number): void {
const arr1 = nums1.slice(0, m), arr2 = nums2;

for (let k = 0, i = 0, j = 0; k < m + n; k++) {
nums1[k] = j >= n || arr1[i] <= arr2[j] ? arr1[i++] : arr2[j++];
}
};

官解的逆向双指针更优秀,空间复杂度为 O(1)O(1),即不需要另外开辟数组辅助,而是优先将结果填充到 nums1 尾部区域

  • 合并排序
var merge = function (nums1, m, nums2, n) {
nums1.splice(m, n, ...nums2);
return nums1.sort((a, b) => a - b);
};

· 阅读需 3 分钟

1、题干

给你两个正整数数组 spells 和 potions ,长度分别为 n 和 m ,其中 spells[i] 表示第 i 个咒语的能量强度,potions[j] 表示第 j 瓶药水的能量强度。

同时给你一个整数 success 。一个咒语和药水的能量强度 相乘 如果 大于等于 success ,那么它们视为一对 成功 的组合。

请你返回一个长度为 n 的整数数组 pairs,其中 pairs[i] 是能跟第 i 个咒语成功组合的 药水 数目。

 

示例 1:

输入:spells = [5,1,3], potions = [1,2,3,4,5], success = 7
输出:[4,0,3]
解释:
- 第 0 个咒语:5 * [1,2,3,4,5] = [5,10,15,20,25] 。总共 4 个成功组合。
- 第 1 个咒语:1 * [1,2,3,4,5] = [1,2,3,4,5] 。总共 0 个成功组合。
- 第 2 个咒语:3 * [1,2,3,4,5] = [3,6,9,12,15] 。总共 3 个成功组合。
所以返回 [4,0,3] 。

示例 2:

输入:spells = [3,1,2], potions = [8,5,8], success = 16
输出:[2,0,2]
解释:
- 第 0 个咒语:3 * [8,5,8] = [24,15,24] 。总共 2 个成功组合。
- 第 1 个咒语:1 * [8,5,8] = [8,5,8] 。总共 0 个成功组合。
- 第 2 个咒语:2 * [8,5,8] = [16,10,16] 。总共 2 个成功组合。
所以返回 [2,0,2] 。

 

提示:

  • n == spells.length
  • m == potions.length
  • 1 <= n, m <= 105
  • 1 <= spells[i], potions[i] <= 105
  • 1 <= success <= 1010

2、思路

potions 排序,遍历 spells 并使用二分查找统计 potions 中不小于 success / spells[i] 的元素数量,复杂度 O(nlogn)O(n\log{n})

3、代码

function successfulPairs(spells: number[], potions: number[], success: number): number[] {
potions.sort((a, b) => a - b);
const pairs = new Array(spells.length).fill(0);

for (let i = 0; i < spells.length; i++) {
const s = success / spells[i];
pairs[i] = search(potions, s);
}

return pairs;
};

function search(arr: number[], k: number) {
let m = 0, l = 0, r = arr.length - 1;
while (l <= r) {
m = (l + r) >> 1;

if (arr[m] >= k) {
r = m - 1;
} else {
l = m + 1;
}
}

return arr.length - l;
}

· 阅读需 2 分钟

1、题干

给定字符串 st ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

致谢:

特别感谢 @pbrother 添加此问题并且创建所有测试用例。

 

示例 1:

输入:s = "abc", t = "ahbgdc"
输出:true

示例 2:

输入:s = "axc", t = "ahbgdc"
输出:false

 

提示:

  • 0 <= s.length <= 100
  • 0 <= t.length <= 10^4
  • 两个字符串都只由小写字符组成。

2、思路

双指针遍历,如果 s 中的字符能全部被消耗完,则结果为 true

3、代码

function isSubsequence(s: string, t: string): boolean {
let i = 0, j = 0;

while (i < s.length && j < t.length) {
while (j < t.length) {
if (t[j++] === s[i]) {
i++;
break;
}
}
}

return i === s.length;
};

· 阅读需 3 分钟

1、题干

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

判题标准:

系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么您的题解将被 通过

 

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

 

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按 非严格递增 排列

2、思路

遍历数组累计唯一数字数量 cc 作为唯一元素下标用于原地修改数组

3、Code

function removeDuplicates(nums: number[]): number {
let c = 1;
for (let i = 1; i < nums.length; i++) {
if (nums[i] !== nums[i - 1]) {
nums[c] = nums[i];
c += 1;
}
}

return c;
};

· 阅读需 4 分钟

1、题干

给你两个字符串 a 和 b ,它们长度相同。请你选择一个下标,将两个字符串都在 相同的下标 分割开。由 a 可以得到两个字符串: aprefix 和 asuffix ,满足 a = aprefix + asuffix ,同理,由 b 可以得到两个字符串 bprefix 和 bsuffix ,满足 b = bprefix + bsuffix 。请你判断 aprefix + bsuffix 或者 bprefix + asuffix 能否构成回文串。

当你将一个字符串 s 分割成 sprefix 和 ssuffix 时, ssuffix 或者 sprefix 可以为空。比方说, s = "abc" 那么 "" + "abc" , "a" + "bc" , "ab" + "c" 和 "abc" + "" 都是合法分割。

如果 能构成回文字符串 ,那么请返回 true,否则返回 false 。

注意, x + y 表示连接字符串 x 和 y 。

 

示例 1:

输入:a = "x", b = "y"
输出:true
解释:如果 a 或者 b 是回文串,那么答案一定为 true ,因为你可以如下分割:
aprefix = "", asuffix = "x"
bprefix = "", bsuffix = "y"
那么 aprefix + bsuffix = "" + "y" = "y" 是回文串。

示例 2:

输入:a = "abdef", b = "fecab"
输出:true

示例 3:

输入:a = "ulacfd", b = "jizalu"
输出:true
解释:在下标为 3 处分割:
aprefix = "ula", asuffix = "cfd"
bprefix = "jiz", bsuffix = "alu"
那么 aprefix + bsuffix = "ula" + "alu" = "ulaalu" 是回文串。

 

提示:

  • 1 <= a.length, b.length <= 105
  • a.length == b.length
  • a 和 b 都只包含小写英文字母

2、思路

这是回文判断的升级版,考虑4种情况:

  • 字符串1是回文
  • 字符串2是回文
  • 字符串1前缀+字符串2后缀是回文
  • 字符串2前缀+字符串1后缀是回文

实际上前面两种情况可以忽略


具体实现时,可以将常规的回文判断做成辅助函数并扩展:

  • 使用两个字符串作为入参,不同于常规回文,这里需要同时判断两串字符
  • 考虑双串组合的情况,增加一个入参 i,用于标识双串组合的起始位置
  • 遍历过程中如果遇到前后不匹配的字符,则考虑两种情况:
    • 双串相等,可以判定不是回文
    • 双串不等,则判断从该位置开始,如果其中任意一个字符串仍符合回文特性,则返回 true,反之为 false

3、代码

function checkPalindromeFormation(s1: string, s2: string): boolean {
function isPdr(a: string, b: string, i: number = 0) {
for (; i < a.length / 2 >> 0; i++) {
if (a[i] !== b[a.length - i - 1]) return a === b ? false : isPdr(a, a, i) || isPdr(b, b, i);
}
return true;
}
return isPdr(s1, s2) || isPdr(s2, s1);
};

4、复杂度

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(1)O(1)

5、执行结果

image.png

· 阅读需 4 分钟

1、题干

给你一个函数  f(x, y) 和一个目标结果 z,函数公式未知,请你计算方程 f(x,y) == z 所有可能的正整数 数对 xy。满足条件的结果数对可以按任意顺序返回。

尽管函数的具体式子未知,但它是单调递增函数,也就是说:

  • f(x, y) < f(x + 1, y)
  • f(x, y) < f(x, y + 1)

函数接口定义如下:

interface CustomFunction {
public:
// Returns some positive integer f(x, y) for two positive integers x and y based on a formula.
int f(int x, int y);
};

你的解决方案将按如下规则进行评判:

  • 判题程序有一个由 CustomFunction9 种实现组成的列表,以及一种为特定的 z 生成所有有效数对的答案的方法。
  • 判题程序接受两个输入:function_id(决定使用哪种实现测试你的代码)以及目标结果 z
  • 判题程序将会调用你实现的 findSolution 并将你的结果与答案进行比较。
  • 如果你的结果与答案相符,那么解决方案将被视作正确答案,即 Accepted

 

示例 1:

输入:function_id = 1, z = 5
输出:[[1,4],[2,3],[3,2],[4,1]]
解释:function_id = 1 暗含的函数式子为 f(x, y) = x + y
以下 x 和 y 满足 f(x, y) 等于 5:
x=1, y=4 -> f(1, 4) = 1 + 4 = 5
x=2, y=3 -> f(2, 3) = 2 + 3 = 5
x=3, y=2 -> f(3, 2) = 3 + 2 = 5
x=4, y=1 -> f(4, 1) = 4 + 1 = 5

示例 2:

输入:function_id = 2, z = 5
输出:[[1,5],[5,1]]
解释:function_id = 2 暗含的函数式子为 f(x, y) = x * y
以下 x 和 y 满足 f(x, y) 等于 5:
x=1, y=5 -> f(1, 5) = 1 * 5 = 5
x=5, y=1 -> f(5, 1) = 5 * 1 = 5

 

提示:

  • 1 <= function_id <= 9
  • 1 <= z <= 100
  • 题目保证 f(x, y) == z 的解处于 1 <= x, y <= 1000 的范围内。
  • 1 <= x, y <= 1000 的前提下,题目保证 f(x, y) 是一个 32 位有符号整数。

题目不难,难的是阅读理解,这题目看了老半天才懂

2、思路1

暴力枚举加上剪枝优化,效果还不错

3、代码

function findSolution(customfunction: CustomFunction, z: number): number[][] {
let ans = [];

for (let x = 1; x < 1001; x++) {
if (customfunction.f(x, 1) > z) break;

for (let y = 1; y < 1001; y++) {
if (customfunction.f(x, y) === z) ans.push([x, y]);
else if (customfunction.f(x, y) > z) break;
}
}

return ans;
};

4、复杂度

  • 时间复杂度:O(n2)O(n^2)
  • 空间复杂度:O(1)O(1)

5、执行结果

image.png

6、思路2

双指针枚举,两个指针相向步进

7、代码

function findSolution(customfunction: CustomFunction, z: number): number[][] {
let ans = [];

for (let x = 1, y = 1000; x < 1001 && y > 0; x++) {
while (y && customfunction.f(x, y) > z) y--;

if (y && customfunction.f(x, y) === z) {
ans.push([x, y]);
y--;
}
}

return ans;
};

8、复杂度

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(1)O(1)

9、执行结果

image.png

· 阅读需 4 分钟

1、题干

一个句子是由一些单词与它们之间的单个空格组成,且句子的开头和结尾没有多余空格。比方说,"Hello World" ,"HELLO" ,"hello world hello world" 都是句子。每个单词都  包含大写和小写英文字母。

如果两个句子 sentence1 和 sentence2 ,可以通过往其中一个句子插入一个任意的句子(可以是空句子)而得到另一个句子,那么我们称这两个句子是 相似的 。比方说,sentence1 = "Hello my name is Jane" 且 sentence2 = "Hello Jane" ,我们可以往 sentence2 中 "Hello" 和 "Jane" 之间插入 "my name is" 得到 sentence1 。

给你两个句子 sentence1 和 sentence2 ,如果 sentence1 sentence2 是相似的,请你返回 true ,否则返回 false 。

 

示例 1:

输入:sentence1 = "My name is Haley", sentence2 = "My Haley"
输出:true
解释:可以往 sentence2 中 "My" 和 "Haley" 之间插入 "name is" ,得到 sentence1 。

示例 2:

输入:sentence1 = "of", sentence2 = "A lot of words"
输出:false
解释:没法往这两个句子中的一个句子只插入一个句子就得到另一个句子。

示例 3:

输入:sentence1 = "Eating right now", sentence2 = "Eating"
输出:true
解释:可以往 sentence2 的结尾插入 "right now" 得到 sentence1 。

示例 4:

输入:sentence1 = "Luky", sentence2 = "Lucccky"
输出:false

 

提示:

  • 1 <= sentence1.length, sentence2.length <= 100
  • sentence1 和 sentence2 都只包含大小写英文字母和空格。
  • sentence1 和 sentence2 中的单词都只由单个空格隔开。

2、思路1

双指针模拟,对比首尾单词是否匹配直至指针相遇

3、代码

function areSentencesSimilar(s1: string, s2: string): boolean {
const words1 = s1.split(' '), words2 = s2.split(' ');
const ml = Math.min(words1.length, words2.length);

let i = 0, j = 0;
while (i < ml && j < ml) {
if (words1[i] === words2[i]) i++;
else if (i < ml - j && words1.at(-j - 1) === words2.at(-j - 1)) j++;
else break;
}

return i + j === ml;
};

4、复杂度

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(n)O(n)

5、执行结果

image.png

6、思路2

双端队列模拟,移除首尾相同单词直至某个队列为空。

  • 没有自带的双端队列,这里使用数组代替,出队时间复杂度会更高
  • 队列的实现比双指针更简单,不用考虑那么多边界问题,下次不折腾了

7、代码

function areSentencesSimilar(s1: string, s2: string): boolean {
const words1 = s1.split(' '), words2 = s2.split(' ');

while (words1.length && words2.length) {
if (words1.at(-1) === words2.at(-1)) words1.pop(), words2.pop();
else if (words1[0] === words2[0]) words1.shift(), words2.shift();
else break;
}

return !words1.length || !words2.length;
};

8、复杂度

  • 时间复杂度:O(n2)O(n^2)
  • 空间复杂度:O(n)O(n)

9、执行结果

image.png

· 阅读需 4 分钟

1、题干

给你一个只包含字符 'a''b' 和 'c' 的字符串 s ,你可以执行下面这个操作(5 个步骤)任意次:

  1. 选择字符串 s 一个 非空 的前缀,这个前缀的所有字符都相同。
  2. 选择字符串 s 一个 非空 的后缀,这个后缀的所有字符都相同。
  3. 前缀和后缀在字符串中任意位置都不能有交集。
  4. 前缀和后缀包含的所有字符都要相同。
  5. 同时删除前缀和后缀。

请你返回对字符串 s 执行上面操作任意次以后(可能 0 次),能得到的 最短长度 。

 

示例 1:

输入:s = "ca"
输出:2
解释:你没法删除任何一个字符,所以字符串长度仍然保持不变。

示例 2:

输入:s = "cabaabac"
输出:0
解释:最优操作序列为:
- 选择前缀 "c" 和后缀 "c" 并删除它们,得到 s = "abaaba" 。
- 选择前缀 "a" 和后缀 "a" 并删除它们,得到 s = "baab" 。
- 选择前缀 "b" 和后缀 "b" 并删除它们,得到 s = "aa" 。
- 选择前缀 "a" 和后缀 "a" 并删除它们,得到 s = "" 。

示例 3:

输入:s = "aabccabba"
输出:3
解释:最优操作序列为:
- 选择前缀 "aa" 和后缀 "a" 并删除它们,得到 s = "bccabb" 。
- 选择前缀 "b" 和后缀 "bb" 并删除它们,得到 s = "cca" 。

 

提示:

  • 1 <= s.length <= 105
  • s 只包含字符 'a''b' 和 'c' 。

2、思路1

双指针遍历直到指针交叉或对应不同字符

3、代码

function minimumLength(s: string): number {
let l = 0, r = s.length - 1;
while (l < r && s[l] === s[r]) {
const c = s[l];
while (l <= r && s[l] === c) l++;
while (l <= r && s[r] === c) r--;
}
return r - l + 1;
};

4、复杂度

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(1)O(1)

5、执行结果

image.png

6、思路2

按题意模拟,剔除首尾相同字符,直到首尾字符不同或字符串长度小于2

7、代码

function minimumLength(s: string): number {
while (s[1] && s[0] === s[s.length - 1]) {
const c = s[0];
while (s[0] === c) s = s.slice(1);
while (s[s.length - 1] === c) s = s.slice(0, s.length - 1);
}
return s.length;
};

8、执行结果

image.png

9、思路3

按题意模拟,借助正则剔除首尾相同字符,直到首尾字符不同或字符串长度小于2

10、代码

function minimumLength(s: string): number {
if (!s[1] || s[0] !== s[s.length - 1]) return s.length;
return minimumLength(s.replace(new RegExp(`^${s[0]}+|${s[0]}+$`, 'g'), ''));
};

11、执行结果

image.png

· 阅读需 3 分钟

1、题干

有时候人们会用重复写一些字母来表示额外的感受,比如 "hello" -> "heeellooo", "hi" -> "hiii"。我们将相邻字母都相同的一串字符定义为相同字母组,例如:"h", "eee", "ll", "ooo"。

对于一个给定的字符串 S ,如果另一个单词能够通过将一些字母组扩张从而使其和 S 相同,我们将这个单词定义为可扩张的(stretchy)。扩张操作定义如下:选择一个字母组(包含字母 c ),然后往其中添加相同的字母 c 使其长度达到 3 或以上。

例如,以 "hello" 为例,我们可以对字母组 "o" 扩张得到 "hellooo",但是无法以同样的方法得到 "helloo" 因为字母组 "oo" 长度小于 3。此外,我们可以进行另一种扩张 "ll" -> "lllll" 以获得 "helllllooo"。如果 s = "helllllooo",那么查询词 "hello" 是可扩张的,因为可以对它执行这两种扩张操作使得 query = "hello" -> "hellooo" -> "helllllooo" = s

输入一组查询单词,输出其中可扩张的单词数量。

 

示例:

输入: 
s = "heeellooo"
words = ["hello", "hi", "helo"]
输出:1
解释
我们能通过扩张 "hello" 的 "e" 和 "o" 来得到 "heeellooo"。
我们不能通过扩张 "helo" 来得到 "heeellooo" 因为 "ll" 的长度小于 3 。

 

提示:

  • 1 <= s.length, words.length <= 100
  • 1 <= words[i].length <= 100
  • s 和所有在 words 中的单词都只由小写字母组成。

Problem: 809. 情感丰富的文字

2、思路

总体思路是:先提取字符串 spattern,然后遍历 words 中每一项是否与 pattern 相匹配。这里使用正则实现。

  • 提取字符串 s 的正则
    • s 中连续 3 个以上相同的字符 a 替换为 a{1,a.count},比如 ooooo 替换为 o{1,5}
    • 替换后的字符串添加首尾标志 ^$ 并转为正则
  • 遍历 words 并与正则匹配,若匹配则结果累计 1

3、Code

function expressiveWords(s: string, words: string[]): number {
const r = s.replace(/(\w)\1{2,}/g, (m, a) => `${a}{1,${m.length}}`);
const reg = new RegExp(`^${r}$`);
return words.reduce((a, c) => a + +reg.test(c), 0);
};

4、执行结果

image.png

· 阅读需 3 分钟

1、题干

神奇字符串 s 仅由 '1''2' 组成,并需要遵守下面的规则:

  • 神奇字符串 s 的神奇之处在于,串联字符串中 '1''2' 的连续出现次数可以生成该字符串。

s 的前几个元素是 s = "1221121221221121122……" 。如果将 s 中连续的若干 12 进行分组,可以得到 "1 22 11 2 1 22 1 22 11 2 11 22 ......" 。每组中 1 或者 2 的出现次数分别是 "1 2 2 1 1 2 1 2 2 1 2 2 ......" 。上面的出现次数正是 s 自身。

给你一个整数 n ,返回在神奇字符串 s 的前 n 个数字中 1 的数目。

 

示例 1:

输入:n = 6
输出:3
解释:神奇字符串 s 的前 6 个元素是 “122112”,它包含三个 1,因此返回 3 。

示例 2:

输入:n = 1
输出:1

 

提示:

  • 1 <= n <= 105

2、解题思路

题目要求计算神奇字符串 s 的前 n 个数字中 1 的数目,只需要按照规则模拟构造神奇字符串,再计算 1 的数量即可。

构造过程借助双指针实现,快指针 i 逐步递增,慢指针 cur 根据其可消耗次数 count 决定是否递增,每次 i 递增做以下计算:

  • 若当前字符是 1,则最终结果累计一次
  • 计算出第 i+1 个字符,i+1 个字符只有两种情况,要么跟第 i 个字符相同,要么不同
    • 当前仅当慢指针 cur 对应的字符是 2 且 第 i-1i 个字符不同时,第 ii+1 个字符相同
    • 其他情况第 ii+1 个字符不同
  • 可消耗次数 count1
  • 若可消耗次数 count 次数为 0,那么慢指针 cur1count 赋值为慢指针指向的新数字

3、代码

function magicalString(n: number): number {
const nums = new Array(n).fill(1);
let res = 0, cur = 0, count = 1;

for (let i = 0; i < n; i++) {
if (nums[i] === 1) res++;

nums[i + 1] = nums[i] === 2 ? 1 : 2;
if (nums[cur] === 2 && nums[i] !== nums[i - 1]) nums[i + 1] = nums[i];

if (--count === 0) count = nums[++cur];
}

return res;
}

4、复杂度

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(n)O(n)

5、执行结果

image.png