【Leetcode刷题】双指针法

双指针法

双指针法其实和划窗是非常类似的,都是用两个指针控制子数组的前端和后端,通过不断的滑动来进行整个数组的分析。
但是具体做法上,不同的问题有不同的做法。

q27:原地移除值为val的元素并且返回长度

快慢指针法

这个问题主要的特点是,不care非val值的位置,只想得到其个数,而且要返回的是将非val值放在数组前面的数组(原地移除,意味着数组地址还是原来那个)。这个题用双指针法是比较好解的,常规思路:
快慢指针法,当快指针指向的数字不是val时,快慢指针一起移动,并且将快指针值赋值给慢指针的位置;当快指针指向的数字是val时,慢指针不动,以指示后面第一个非val值应该处在的位置,终止条件也非常清晰:当fast走到数组最后的时候,slow走到了最后一个非val的位置,此时slow后面的元素都可以不要了。
代码如下:

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow = 0
        for fast in range(len(nums)):
            if not nums[fast] == val:
                nums[slow] = nums[fast]
                slow += 1
        return slow

这种方式确实比较精妙而直接,很好地体现了快慢指针法的用途:快指针用于遍历全数组,而慢指针则用于定位想要锚定住的位置(此题中为非val的位置)。

首尾指针法

但是这道题还可以用另一种方式来解,观察到题目中,不care非val值的位置,非val子数组之后的值也无所谓,因此我想到是否可以用置换的方法来做,即一头一尾两个指针,头指针不断右移,当碰到val时,指挥尾指针往左移,当尾指针碰到非val值时,头尾指针的值进行互换,完成一次“删除”。终止条件就是左右指针相遇,问题解决,此时,右指针所在的位置就是我们想要的分割点。

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        left = 0
        right = len(nums)

        while(right > left):
            if nums[left] == val:
                right -= 1
                while(nums[right] == val and right > left):
                    right -= 1
                if right == left:
                    break
                nums[left] = nums[right]
                nums[right] = val
            else:
                left += 1
        return right

这种方式比前一种更快一些,因为总共只需要搜索一次全数组,但是这种方法的局限性很强,只能用在这种允许首尾交换的问题上。而快慢指针法则用途更加广泛一些。

q977:对平方数进行排序

这个问题我一开始的做法是,先确定中间那个正负值的分割点,然后指针从分割点出发,一个向左一个向右,进行比较和排序,这种做法在循环终止之后,还要考虑其中一半儿用完了怎么整,步骤稍微多了点儿,但是没用完那部分就不用比较了,相对来说节省了计算开销。

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        squares = []
        for num in nums:
            squares.append(num * num)

        if nums[0] >= 0:
            return squares
        if nums[-1] <= 0:
            return squares[::-1]

        right = 0
        for ind in range(len(nums)):
            if nums[ind] == 0:
                right = ind
                break
            elif nums[ind - 1] < 0 and nums[ind] > 0:
                right = ind
                break
        new_squares = []
        left = right - 1

        while(left > -1 and right < len(squares)):
            val_left = squares[left]
            val_right = squares[right]

            if val_left < val_right:
                new_squares.append(val_left)
                left -= 1
            elif val_left > val_right:
                new_squares.append(val_right)
                right += 1
            else:
                new_squares.append(val_left)
                new_squares.append(val_right)
                left -= 1
                right += 1
        if left == -1 and right != len(squares):
            for ind in range(right, len(squares)):
                new_squares.append(squares[ind])
        elif right == len(squares) and left != -1:
            for ind in range(0, left + 1):
                new_squares.append(squares[left - ind])
        return new_squares

随后我看到了一种更为精妙的做法:从完全平方数数组的头尾开始(首尾指针法!),反向填充数组,这样就不用求分割点,不用考虑其中一侧用完之后怎么办了,bravo!

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        squares = []
        for num in nums:
            squares.append(num * num)

        if nums[0] >= 0:
            return squares
        if nums[-1] <= 0:
            return squares[::-1]

        new_squares = [0] * len(squares)
        left = 0
        right = len(nums) - 1
        total_ind = len(nums) - 1

        while(right >= left):
            s_left = squares[left]
            s_right = squares[right]

            if s_left <= s_right:
                new_squares[total_ind] = s_right
                total_ind -= 1
                right -= 1
            else:
                new_squares[total_ind] = s_left
                total_ind -= 1
                left += 1
        return new_squares

q844:比较含退格的字符串

这题最直接的方式就是在原始字符串上做原地修改,用快慢指针法去掉退格字符,剩下有效字符,然后进行比较,但是这种方法有个明显的坏处:需要做两次循环,先去退格,然后再比较。能否在比较的同时考虑退格字符的存在?由于有退格字符的存在,从前往后比较是不科学的,因此最好的方式就是从后往前。
我的思路是:设置一个寻找有效字符的函数,当碰到#时,就启动该函数,该函数可以被视作一个指针滑动机制,从#滑动到第一个有效字符。但是要注意的是,有效字符的判定应该是在此之前,#和有效字符达到数值上的平衡,为此我写了一些函数来解决这个问题。

class Solution:
    def backspaceCompare(self, s: str, t: str) -> bool:
        equalFlag = True
        ind_s = len(s) - 1
        ind_t = len(t) - 1
        while(equalFlag and (ind_s > -1 or ind_t > -1)):
            if ind_s <= -1:
                cmp_char_s = ''
            elif s[ind_s] == "#":
                ind_s = getEffChar(s, ind_s)
                if ind_s > -1:
                    cmp_char_s = s[ind_s]
                else:
                    cmp_char_s = ''
            else:
                cmp_char_s = s[ind_s]

            if ind_t <= -1:
                cmp_char_t = ""
            elif t[ind_t] == "#":
                ind_t = getEffChar(t, ind_t)
                if ind_t > -1:
                    cmp_char_t = t[ind_t]
                else:
                    cmp_char_t = ''
            else:
                cmp_char_t = t[ind_t]

            ind_s -= 1
            ind_t -= 1

            if not cmp_char_s == cmp_char_t:
                equalFlag = False
        return equalFlag

def getEffChar(s: str, ind: int) -> int:
    while(s[ind] == "#" and ind >= 0):
        ind = getIndAfterBackspace(s, ind)
    return ind

def getIndAfterBackspace(s: str, ind: int) -> int:
    count_c = 0
    count_bs = 1
    while(not count_c == count_bs):
        ind -= 1
        if ind < 0:
            return -1
        if s[ind] == "#":
            count_bs += 1
        else:
            count_c += 1
    return max(ind - 1, -1)

官方提供的解法实际上和我们的思想一致,但是他们做得更加简洁,究其原因是我们其实一开始考虑的太简单,直接考虑了#和字符数量配对的情况,当发现有不配对的情况时,只是在原始思路上进行补充,而没有很好地跳出来,用更精简的思路去解。整体思路还是比较一致的,就不再赘述了。

209:长度最小的子数组

209这道题也是用快慢指针法求解的,具体的做法就不再赘述,解题笔记中有。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值