leetcode:双指针法之-有序数组-去重与合并

21. 合并两个有序链表

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

非常正常的思路,定义两个指针分别指向两个链表,然后分别进行比较,

序号0123456
链表aa0a1a2a3a4a5a6
链表bb0b1b2b3b4b5b6

先比较a0和b0,a0大于b0的话,就把链表a的指针往后挪一下,指向a1,接着比较a1和b0,如果a1还是小于b0,链表a的指针再往后挪一个,比较a2和b0,如果a2大于了b0,就把链表b的指针往后挪一下,指向b1,接着比较a2和b1,一直比较到最后,这是非常朴素的思想

# Definition for singly-linked list.
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        head = tmp = ListNode(-1)
        while l1 and l2:
            if l1.val < l2.val:
                tmp.next = l1
                l1 = l1.next
            else:
                tmp.next = l2
                l2 = l2.next
            tmp = tmp.next
        if l1:
            tmp.next = l1
        if l2:
            tmp.next = l2
        return head.next

这里要求的是新链表是通过拼接给定的两个链表的所有节点组成的。 所以没有新建链表,直接把原来链表拼接上去

88. 合并两个有序数组

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]

合并两个数组的方法其实和合并链表是一模一样的,但是数组有一个好处是可以直接读取和存储任意位置,我们可以倒序比较,先比较两个数组中最后的位置,也就是最大的值,把两个数组中最大值放在最后面,这样我们就可以不新增空间的情况下,把两个数组存到一个里面,当然这里也是有限制条件的 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        end = m + n - 1
        p = m - 1
        q = n - 1
        while p >= 0 and q >= 0:
            if nums1[p] > nums2[q]:
                nums1[end] = nums1[p]
                p -= 1
            else:
                nums1[end] = nums2[q]
                q -= 1
            end -= 1

        nums1[:q+1] = nums2[:q+1]

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

这个算得上是双指针的一个巧妙应用了,前两个其实是正常的应用。取两个指针,i,j,如果nums[j] == nums[i]说明重复了,我们把 j j j移动一位,接着再进行比较,如果还是相等就继续移动,等到nums[j] 不等于nums[i]时说明是一个新的项了,咱们把i的位置往后挪一位,把这一项赋值给nums[i]

本质上nums[j]用来发现不同项,发现之后就存到前面去

nums[j] == nums[i],j = j+1

序号ij
数组2233334
序号ij
数组2233334

发现nums[j] != nums[i] ,i=i+1,并将值赋值过去

序号ij
数组2333334

接着,发现nums[j] == nums[i],j = j+1

序号ij
数组2333334

还是相等j = j+1

序号ij
数组2333334

还是相等j = j+1

序号ij
数组2333334

还是相等j = j+1

序号ij
数组2333334

发现nums[j] != nums[i] ,i=i+1,并将值赋值过去

序号ij
数组2343334

结束之后前i位保存的就是去重后的数据了

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        i,j,n = 0,1,len(nums)
        while j < n:
            if nums[j] == nums[i]:
                j += 1
            else:
                i += 1
                nums[i] = nums[j]
        return i+1

27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

同样是双指针,一个指针指向头,一个指针指向尾,如果头指针的值不等于val,就往后走,如果等于val,咱们就从后面找,找到一个不等与val的值,替换过去

序号ij
数组01223042

val[i] != 2,就往后走

序号ij
数组01223042

走到这里之后等于2了,然后看j,发现nums[j] == 2,直接往前走,把这个2给丢弃了

序号ij
数组01223042

发现nums[j] != 2,把这个值放到之前nums[i]==2的位置,然后j再往前走,i往后走

序号ij
数组01423042

nums[i]又等于 2了,接着去找不等于2的j,进行替换

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        p,q = 0,len(nums)-1
        while p <= q:
            if nums[q] == val:
                q -= 1
            if nums[p] != val:
                p += 1
            else:
                nums[p] = nums[q]
                q -= 1
        return p

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值