【算法-面试】数组双指针专题

说明

这里的双指针 针对于数组的形式展开
其他形式的双指针如滑动窗口如【算法-面试】子串专题
【算法-面试】二分搜索专题

# coding = "utf-8"

'''
数组

常见思路:
    快慢指针
    滑动窗口
'''


def reverse_string(s):
    '''
    翻转数组
    思路:
        双指针
    '''
    l, r = 0, len(s) - 1
    while l < r:
        temp = s[l]
        s[l] = s[r]
        s[r] = temp
        l += 1
        r -= 1
    return s


def remove_duplicates(nums):
    '''
    给你⼀个有序数组 nums,请你原地删除重复出现的元素,使每个元素只出现⼀次,返回删除后数组的新⻓度。
    leetcode: 26
    input: nums = [1,1,2]
    output: 2, nums = [1,2]
    思路:
        1. 使用快慢指针
        2. 快指针先走,当快慢指针指向的值不相等时,更新慢指针
    '''
    if len(nums) == 0:
        return 0
    slow, fast = 0, 0
    while fast <= len(nums) - 1:
        if nums[fast] != nums[slow]:
            nums[slow] = nums[fast]
            slow += 1
        fast += 1
    return slow + 1


def remove_element(nums, val):
    '''
    给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
    leetcode: 27
    input: nums = [3,2,2,3], val = 3
    output:2, nums = [2,2]
    思路:
        1. 快慢指针
    '''
    if len(nums) == 0:
        return 0
    slow, fast = 0, 0
    while fast < len(nums):
        if nums[fast] != val:
            nums[slow] = nums[fast]
            slow += 1
        fast += 1
    print(nums[:slow])
    return slow


def remove_zero(nums):
    '''
    给定⼀个数组 nums,编写⼀个函数将所有 0 移动到数组的末尾,必须在原数组上操作,同时保持⾮零元素的相对顺序。
    leetcode: 283
    input: [0,1,0,3,12]
    output:[1,3,12,0,0]
    思路:
        1. 快慢指针
        2. 当快指针不等于0时,慢指针指向的值为快指针值
        3. 终止条件时快指针到结尾
        4. 根据慢指针指向的值,将末尾的部分全部补充为0
    '''
    slow, fast = 0, 0
    while fast < len(nums):
        if nums[fast] != 0:
            nums[slow] = nums[fast]
            slow += 1
        fast += 1
    while slow < len(nums):
        nums[slow] = 0
        slow += 1
    return nums


def water_trap(height):
    '''
    给定 n 个⾮负整数表示每个宽度为 1 的柱⼦的⾼度图,计算按此排列的柱⼦,下⾬之后能接多少⾬⽔。
    leetcode: 42
    input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
    output:6
    思路:
        1. 左右指针,向中间靠拢
        2. 靠拢的时候,分别记录两边的最大值
        3. 根据左右两遍的最大值相比大小,选取其中小的值,与当前指针对应的height进行做差值,即得到当前指针对应的接到雨水的值
        4. while终止条件为左指针大于右指针
    '''
    n = len(height)
    l, r = 0, n - 1  # 左右指针
    l_max, r_max, res = height[0], height[n - 1], 0  # 左侧最大值  右侧最大值  结果保存
    while l <= r:
        l_max = max(l_max, height[l])
        r_max = max(r_max, height[r])

        if l_max < r_max:
            res += l_max - height[l]
            l += 1
        else:
            res += r_max - height[r]
            r -= 1
    return res


def intervalIntersection(firstList, secondList):
    '''
    给定两个由一些 闭区间 组成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。
    每个区间列表都是成对 不相交 的,并且 已经排序 。返回这两个区间列表的交集 。
    leetcode: 986
    input: firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
    output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
    思路:
        1. 针对一段区间firstList的[a1, a2], secondList的[b1, b2], 核心思路是c1=max(a1, b1), c2=min(a2, b2)
        2. 设置变量双指针i,j,分表代表firstList 和 secondList
        3. 前进条件:当 b2<a2 的时候,j++; 反之i++
        4. 终止条件:i>=len(firstList) && j>=len(secondList)
    '''
    i, j, res = 0, 0, []
    while i < len(firstList) and j < len(secondList):
        a1, a2 = firstList[i][0], firstList[i][1]
        b1, b2 = secondList[j][0], secondList[j][1]
        if b2 >= a1 and a2 >= b1:  # 保证是有交集的
            res.append([max(a1, b1), min(a2, b2)])
        if b2 < a2:
            j += 1
        else:
            i += 1
    return res


def advantageCount(nums1, nums2):
    '''
    给定两个大小相等的数组 A 和 B,A 相对于 B 的优势可以用满足 A[i] > B[i] 的索引 i 的数目来描述。
    返回 A 的任意排列,使其相对于 B 的优势最大化。
    leetcode: 870
    input: A = [2,7,11,15], B = [1,10,4,11]
    output: [2,11,7,15]
    思路:
        1. 将不改变原来的结构的降序后的[i, nums2]组放入队列中max_q,将nums1升序排序
        2. 定义r指针,代表nums1的最右端(最大值)的指针; l指针,代表nums1最左端(最小值)的指针
        3. 比较nums1[r] 和 取出的max_q[0]进行逐一比较
        4. 如果 nums1[r]>max_q[0],res[i]中保存nums1[r], r--;反之,res[i]保存nums1[l],l++
        5. 终止条件:max_q为空
    '''
    from queue import PriorityQueue
    class PQ(object):
        def __init__(self, idx, val):
            self.idx = idx
            self.val = val

        def __lt__(self, other):
            return self.val > other.val

    n = len(nums2)
    l, r, res, max_q = 0, n - 1, [], PriorityQueue()
    for i, num in enumerate(nums2):
        max_q.put(PQ(i, num))
        res.append(0)
    # max_q = sorted(max_q, key=lambda x: x[1], reverse=True)
    nums1 = sorted(nums1)
    print(max_q, nums1)

    while not max_q.empty():
        m = max_q.get()
        i, max_value = m.idx, m.val
        if nums1[r] > max_value:
            res[i] = nums1[r]
            r -= 1
        else:
            res[i] = nums1[l]
            l += 1
    return res


def threeSum(nums):
    '''
    给你⼀个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c,使得 a + b + c = 0? 请你找出所有和为 0 且不重复的三元组。
    leetcode: 15
    input: nums = [-1,0,1,2,-1,-4]
    output: [[-1,-1,2],[-1,0,1]]
    思路:
        1. 先排序,后双指针
        2. res中是一个三元组,[[x,x,x],[x,x,x]]
        3. 转换为twosum问题,使用双指针向中间靠拢,当sum值于target相等时,则保存该双元组
        4. 优化:while循环避免重复元素
    '''

    def towSum(arr, start, target):
        l, r = start, len(arr) - 1
        tups = []
        while l < r:
            left, right = arr[l], arr[r]
            s = left + right
            if s < target:
                while l < r and nums[l] == left:  # 避免重复元素
                    l += 1
            elif s > target:
                while l < r and nums[r] == right:
                    r -= 1
            else:
                tups.append([left, right])
                while l < r and nums[l] == left:
                    l += 1
                while l < r and nums[r] == right:
                    r -= 1
        return tups

    nums = sorted(nums)  # 排序
    i, n, res = 0, len(nums), []
    while i < n:
        tups = towSum(nums, start=i, target=-nums[i])
        for t in tups:
            t.append(nums[i])
            res.append(t)
        while i < n - 1 and nums[i] == nums[i + 1]:
            i += 1
        i += 1
    return res


if __name__ == "__main__":
    # print(reverse_string(["h", "e", "l", "l", "o"]))
    # print(remove_duplicates([1, 1, 2]))
    # print(remove_element([3, 2, 2, 3], 3))
    # print(remove_zero([0, 1, 0, 3, 12]))
    # print(water_trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]))
    # print(intervalIntersection([[0, 2], [5, 10], [13, 23], [24, 25]], [[1, 5], [8, 12], [15, 24], [25, 26]]))
    # print(advantageCount([2, 7, 11, 15], [1, 10, 4, 11]))
    print(threeSum([-1, 0, 1, 2, -1, -4]))

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值