数组第二课,删除元素。

27.移除元素

删除数组中的元素主要还是“覆盖”。这也是我第一次写双指针。

我的解法是使用头尾双指针,遇到要删除的元素就交换两个指针指向的元素。

Invariant:头指针之前的元素不含需要删除的值。

思路没问题,和题解一样是最优的一次遍历。但是写得太难看了:

def removeElement(self, nums: List[int], val: int) -> int:
    tail = len(nums) - 1
    for i in range(len(nums)):
        if tail < i:
            break
        if nums[i] == val:
            while nums[tail] == val:
                tail -= 1
                if tail < i:
                    return tail + 1
            nums[i], nums[tail] = nums[tail], nums[i]
            tail -= 1
    return tail + 1

改进之后:

def removeElement(self, nums: List[int], val: int) -> int:
    head = 0
    tail = len(nums) - 1
    while tail >= head:
        if nums[head] == val:
            nums[head] = nums[tail]
            tail -= 1
        else:
            head += 1
    return tail + 1

总结:双指针就不要用迭代器遍历了,画蛇添足。旧代码的while可以优化也可以不优化,时间是一样的。

如果要保留元素相对位置?

两个指针从头开始。Invariant仍然是:头指针之前的元素不含需要删除的值。

def removeElement(self, nums: List[int], val: int) -> int:
    left = 0
    for right in range(len(nums)):
        nums[left] = nums[right]
        if nums[left] == val:
            # 这个方法的精髓就是:写一句continue就相当于把nums[left]这个元素给删除了。
            continue
        left += 1
    return left

这边因为快指针和迭代器的行为是一样的所以偷懒了。

26. 删除有序数组中的重复项

思路和之前第二种双指针相同,判断条件变成了

def removeDuplicates(self, nums: List[int]) -> int:
    if not nums:
        return 0
    left = 0
    lastUniqueValue = nums[0]
    for right in range(len(nums)):
        nums[left] = nums[right]
        if not left == 0 and nums[left] == lastUniqueValue:
            continue
        else:
            lastUniqueValue = nums[left]
        left += 1
    return left

283. 移动零

帽子戏法,这次的条件是碰到0就删。

def moveZeroes(self, nums: List[int]) -> None:
    left = 0
    for right in range(len(nums)):
        nums[left] = nums[right]
        if nums[left] == 0:
            continue
        left += 1
    nums[left:] = [0] * (len(nums)-left)

844. 比较含退格的字符串

最intuitive的解法是用栈。

双指针解法:竟然是反向双指针,无奈了。

官方题解

977. 有序数组的平方

通过观察,绝对值最大的在两头,所以头尾双指针。

特例:纯正数或者纯负数。这时只要让一边的指针动即可。

def sortedSquares(self, nums: List[int]) -> List[int]:
    deque = collections.deque()
    left = 0
    right = len(nums) - 1
    while left <= right:
        sqleft = nums[left] ** 2
        sqright = nums[right] ** 2
        if sqleft >= sqright:
            deque.appendleft(sqleft)
            left += 1
        else:
            deque.appendleft(sqright)
            right -= 1
    return list(deque)

优化:一个好的deque的insertion应该是Amortized O(1)。当然因为本题中返回数组长度固定,也可以预先创一个数组然后追踪插入的index,这样就是纯纯的O(1)了。

总结

前三例是删除元素,记住它们的解法。

后两例不算删除元素,但也是双指针。