跳到主要内容

双指针

以下是有关双指针相关算法的题目与题解。

移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

我的解法

var removeElement = function (nums, val) {
for (let i = 0; i < nums.length; i++) {
if (nums[i] === val) {
nums.splice(i, 1)
i--
}
}

return nums.length
}

然而 splice 操作时间复杂度为 O(n),并且每次删除一个元素都会导致数组的重新排序。因此在算法解题中应尽量避免使用数组自带方法操作

方法: 双指针

var removeElement = function (nums, val) {
let left = 0,
right = nums.length
while (left < right) {
if (nums[left] === val) {
nums[left] = nums[right - 1]
right--
} else {
left++
}
}
return left
}

如果题目有要求排序的话,不如将符合条件(nums[i] !== val)的元素移到数组头部,这样就不需要排序了。

var removeElement = function (nums, val) {
const n = nums.length
let left = 0

for (let right = 0; right < n; right++) {
if (nums[right] !== val) {
nums[left] = nums[right]
left++
}
}

return left
}

删除有序数组中的重复项

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

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

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

我的解法

var removeDuplicates = function (nums) {
const counter = {}
let count = 0
for (let i = 0; i < nums.length; i++) {
counter[nums[i]] = (counter[nums[i]] || 0) + 1

if (counter[nums[i]] > 2) {
continue
} else {
nums[count] = nums[i]
count++
}
}

return count
}

思路很清晰,就是用一个计数对象来计数,然后遍历数组,如果计数大于 1,就跳过,否则就赋值。不过我这里引入了 counter 对象,因此空间复杂度为 O(k),其中 k 表示不同元素的数量。

如果以我这个思路去解决 删除有序数组中的重复项 II 解决题目就会特别容易,只需要将 > 1 换成 1 即可。不过题目要求 不使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

并且通常哈希计数针对无序而言,而题目所给定的数据又有序,因此想要解得好就需要采用其他方案。

方法: 双指针

由于题目声明输入 升序排列 的数组,因此采用快慢双指针来判断后一个元素是否等于前一个,如果不相等则说明是不同的元素,慢指针(不同数字次数)加一,快指针继续遍历。

var removeDuplicates = function (nums) {
const n = nums.length

let fast = 1,
slow = 1
while (fast < n) {
if (nums[fast] !== nums[fast - 1]) {
nums[slow] = nums[fast]
++slow
}
++fast
}
return slow
}

判断子序列

我的解法(失败)

既然判断子序列,那我就把无关元素删除,然后判断最后元素是否包含即可。

var isSubsequence = function (s, t) {
t = t.replace(new RegExp(`[^${s}]`, 'g'), '')
return t.includes(s)
}

然而假设输入数据为

s = "leetcode"
t = "yyyyyleeeytcode"

处理后数据为

s = "leetcode"
t = "leeetcode"

很显然这里 includes 无法判断子序列,因此这种思路不可行。

方法: 双指针

var isSubsequence = function (s, t) {
let i = 0,
j = 0
while (i < s.length && j < t.length) {
if (s[i] === t[j]) {
i++
}
j++
}

return i === s.length
}

两数之和 II - 输入有序数组

方法: 双指针

function twoSum(nums, target) {
let left = 0
let right = nums.length - 1

while (left < right) {
const sum = nums[left] + nums[right]
if (sum === target) {
return [left, right]
}

if (sum > target) {
right--
} else if (sum < target) {
left++
}
}
}