zoukankan      html  css  js  c++  java
  • leetcode刷题-- 1. 双指针

    这里的题是根据 CS-Notes里的顺序来一步步复习。

    双指针

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

    题目描述

    给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

    函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

    说明:

    返回的下标值(index1 和 index2)不是从零开始的。
    你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

    示例:

    输入: numbers = [2, 7, 11, 15], target = 9
    输出: [1,2]
    解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
    

    题解(python)

    class Solution(object):
        def twoSum(self, numbers, target):
            """
            :type numbers: List[int]
            :type target: int
            :rtype: List[int]
            """
            if (numbers == None):
                return None
            i = 0
            j = len(numbers)-1
            while(i<j):
                s = numbers[i] + numbers[j]
                if ( s == target):
                    return [i+1,j+1]
                elif(s < target):
                    i = i + 1
                else:
                    j = j - 1
            return [-1,-1]
    

    1 两数之和 (这道不是双指针,一遍哈希表)

    题目描述

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

    你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

    示例:

    给定 nums = [2, 7, 11, 15], target = 9
    
    因为 nums[0] + nums[1] = 2 + 7 = 9
    所以返回 [0, 1]
    

    题解

    # 用一遍哈希表,也就是python里的字典
    # 将每个数先存进去,相同的key,会因为hash_map[n]=i这句而覆盖掉,边存便判断hash_map里是否存在满足条件的key
    class Solution:
        def twoSum(self, nums: List[int], target: int) -> List[int]:
            hash_map = {}
            for i,n in enumerate(nums):
                if (target-n) in hash_map.keys():
                    return [hash_map[target-n], i]
                hash_map[n] = i
            return [-1,-1]
    

    633平方数之和

    题目描述

    给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c。

    示例1:

    输入: 5
    输出: True
    解释: 1 * 1 + 2 * 2 = 5
    

    示例2:

    输入: 3
    输出: False
    

    题解

    class Solution:
        def judgeSquareSum(self, c: int) -> bool:
            i = 0
            j = int(math.sqrt(c))
    
            while(i<=j):
                s = i*i + j*j
                if(s == c):
                    return True
                elif(s > c):
                    j = j - 1
                else:
                    i = i + 1
            
            return False
    

    680验证回文字符串 Ⅱ

    题目描述

    给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

    示例 1:

    输入: "aba"
    输出: True
    

    示例 2:

    输入: "abca"
    输出: True
    

    解释: 你可以删除c字符。

    注意:

    字符串只包含从 a-z 的小写字母。字符串的最大长度是50000。

    题解

    解法一

    class Solution:
        def validPalindrome(self, s: str) -> bool:            
            if(s[::-1] == s):
                return True
            else:
                n = len(s)
                i,j = 0,n-1
                while(i<j):
                    if(s[i]==s[j]):
                        i += 1
                        j -= 1
                        continue
                    else:
                        a = s[i+1:j+1]
                        b = s[i:j]
                        return a[::-1]==a or b[::-1] == b
    

    解法二

    class Solution:
        def validPalindrome(self, s: str) -> bool:            
            if s == s[::-1]:
                return True
            i,j = 0, len(s) - 1
            while i<j:
                if s[i]==s[j]:
                    i, j = i+1, j-1
                else:
                    a = s[i+1: j+1]
                    b = s[i: j]
                    return a == a[::-1] or b == b[::-1]
    

    python里判断回文很简单a == a[::-1]就行,而这里我发现a[::-1] == a比 a == a[::-1]要耗时更多。

    这道题关键就是删去一个字符

    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]
    

    题解

    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.
            """
            i,j,k = m-1, n-1, m+n-1
            while i>=0 or j>=0:
                if i<0:
                    nums1[k] = nums2[j]
                    k -= 1
                    j -= 1
                elif j<0:
                    nums1[k] = nums1[i]
                    k -= 1
                    i -= 1
                elif nums1[i] > nums2[j]:
                    nums1[k] = nums1[i]
                    k -= 1
                    i -= 1
                else:
                    nums1[k] = nums2[j]
                    k -= 1
                    j -= 1
            
    
    # 另一种32 ms
    class Solution:
        def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
            p1=m-1
            p2=n-1
            p=m+n-1
            while p1>=0 and p2>=0:
                if nums1[p1]>nums2[p2]:
                    nums1[p]=nums1[p1]
                    p1-=1
                else:
                    nums1[p]=nums2[p2]
                    p2-=1
                p-=1
            print(p2)
            if p2>=0:
                nums1[:p2+1]=nums2[:p2+1]
    

    难点在于从后开始往前扫。这个32ms的例子的思想在于:p每移动一格,p1或p2肯定会移动一格去替换掉p处的值,当p1或者p2变成-1移到末尾时,分两种情况:

    • p1 为 -1, 这时将nums2剩下元素填入即nums1[:p2+1] = nums2[:p2+1]
    • p2 为 -1, 这时nums1剩下元素基本不用动。
    • 或者考虑两种极端情况:nums1 = [1,2,3,0,0,0] nums2= [4,5,6]; nums1 = [4,5,6,0,0,0] nums2 = [1,2,3]也是如此

    141环形链表

    题目描述

    给定一个链表,判断链表中是否有环。

    为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

    示例 1:

    输入:head = [3,2,0,-4], pos = 1
    输出:true
    解释:链表中有一个环,其尾部连接到第二个节点。
    

    示例 2:

    输入:head = [1,2], pos = 0
    输出:true
    解释:链表中有一个环,其尾部连接到第一个节点。
    

    示例 3:

    输入:head = [1], pos = -1
    输出:false
    解释:链表中没有环。
    

    题解

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def hasCycle(self, head: ListNode) -> bool:
            # 问题在于NoneType没有next这个attribute
            if head == None:
                return False
            
            l,r = head, head.next
            while r!=None and r.next!=None:
                if l==r:
                    return True
                else:
                    l = l.next
                    r = r.next.next
            return False
    

    使用双指针,快慢指针。一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。

    一开始l,r分别初始化为head和head.next,l走得慢,r走得快。如果在前走的快的r还与l相遇了,那么肯定存在环。

    这里的r只需要判断r和r.next不为None就可以了。判断r的目的是为了r.next不报错不然r.next直接报错。
    需注意,一开始给l,r赋值之前要判断下head是否为None,不然head.next直接报错。

    *142环形链表 II(附加题,熟悉下链表)

    给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

    为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

    说明:不允许修改给定的链表。

    示例 1:

    输入:head = [3,2,0,-4], pos = 1
    输出:tail connects to node index 1
    解释:链表中有一个环,其尾部连接到第二个节点。
    

    示例 2:

    输入:head = [1,2], pos = 0
    输出:tail connects to node index 0
    解释:链表中有一个环,其尾部连接到第一个节点。
    

    示例 3:

    输入:head = [1], pos = -1
    输出:no cycle
    解释:链表中没有环。
    

    题解

    一开始没想出来,参考讨论区里的解法

    解题思路:

    • 这类链表题目一般都是使用双指针法解决的,例如寻找距离尾部第K个节点、寻找环入口、寻找公共尾部入口等。

    算法流程:

    1. 双指针第一次相遇: 设两指针 fast,slow 指向链表头部 head,fast 每轮走 22 步,slow 每轮走 11 步;

      1. 第一种结果: fast 指针走过链表末端,说明链表无环,直接返回 null;
        • TIPS: 若有环,两指针一定会相遇。因为每走 1 轮,fast 与 slow 的间距 +1,fast 终会追上 slow;
      2. 第二种结果: 当fast == slow时, 两指针在环中 第一次相遇 。下面分析此时fast 与 slow走过的 步数关系 :
        • 设链表共有 a+b 个节点,其中 链表头部到链表入口 有 a个节点(不计链表入口节点), 链表环 有 b 个节点(这里需要注意,a 和 b 是未知数,例如图解上链表 a=4,b=5b=5);设两
          指针分别走了 ff,ss 步,则有:
        1. fast 走的步数是slow步数的 2 倍,即 f = 2s;(解析: fast 每轮走 2 步)
        2. fast 比 slow多走了 n 个环的长度,即 f = s + nb;( 解析: 双指针都走过 a 步,然后在环内绕圈直到重合,重合时 fast 比 slow 多走环的长度整数倍 );
        • 以上两式相减得:f = 2nb,s = nb,即fast和slow 指针分别走了 2n,n 个 环的周长 (注意: n 是未知数,不同链表的情况不同)。
    2. 目前情况分析::

      • 如果让指针从链表头部一直向前走并统计步数k,那么所有 走到链表入口节点时的步数 是:k=a+nb(先走 a 步到入口节点,之后每绕 1 圈环( b 步)都会再次到入口节点)。
      • 而目前,slow 指针走过的步数为 nb 步。因此,我们只要想办法让 slow 再走 a 步停下来,就可以到环的入口。
      • 但是我们不知道 a 的值,该怎么办?依然是使用双指针法。我们构建一个指针,此指针需要有以下性质:此指针和slow 一起向前走 a 步后,两者在入口节点重合。那么从哪里走到入口节点需
        要 a 步?答案是链表头部head。
    3. 双指针第二次相遇:

      • slow指针 位置不变 ,将fast指针重新 指向链表头部节点 ;slow和fast同时每轮向前走 11 步;
        • TIPS:此时 f = 0f=0,s = nbs=nb ;
      • 当 fast 指针走到f = af=a 步时,slow 指针走到步s = a+nbs=a+nb,此时 两指针重合,并同时指向链表环入口 。
    4. 返回slow指针指向的节点。

    复杂度分析:

    时间复杂度 O(N):第二次相遇中,慢指针须走步数 a < a + b;第一次相遇中,慢指针须走步数 a + b - x < a + b,其中 x 为双指针重合点与环入口距离;因此总体为线性复杂度;
    空间复杂度 O(1):双指针使用常数大小的额外空间。
    

    当走了a+nb时,位置为入口处;而第一次相遇后慢指针走的距离为nb,注意这两个n不相等,但nb再走a步就到了入口处,这时我们可以另设一个指针从head处走,每次一步,那么他就会与慢指针在入口处相遇。

    题解

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def detectCycle(self, head: ListNode) -> ListNode:
            fast, slow = head, head
            while True:
                if not(fast and fast.next):
                    return 
                fast, slow = fast.next.next, slow.next
                if fast == slow:
                    break
            fast = head
            while fast!=slow:
                fast = fast.next
                slow = slow.next
            return slow
    

    这里我之前在while里判断fast != None and fast.next != None,这样造成的问题就是判断为False时,我的返回一个空说明没环,不好写。所以判断最好还是在if里,循环用while True。

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def detectCycle(self, head: ListNode) -> ListNode:
            if head == None:
                return None
            fast, slow = head, head
            if fast.next!=None:
                fast = fast.next.next
                slow = slow.next
            while fast != None and fast.next != None:
                if fast!=slow:
                    fast = fast.next.next
                    slow = slow.next
                else:
                    fast = head
                    while fast!=slow:
                        fast = fast.next
                        slow = slow.next
                    return slow
    
            return None
    

    524通过删除字母匹配到字典里最长单词

    题目描述

    给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。

    示例 1:

    输入:
    s = "abpcplea", d = ["ale","apple","monkey","plea"]
    
    输出: 
    "apple"
    

    示例 2:

    输入:
    s = "abpcplea", d = ["a","b","c"]
    
    输出: 
    "a"
    

    说明:

    1. 所有输入的字符串只包含小写字母。
    2. 字典的大小不会超过 1000。
    3. 所有输入的字符串长度不会超过 1000。

    题解

    def isSubStr(s:str, t:str):
        #第一种,双指针
        # l1,l2 = len(s), len(t)
        # if l1 < l2:
        #     return False
        # i,j = 0,0
        # while (i<l1 and j<l2):
        #     if (s[i] != t[j]):
        #         i += 1
        #     else:
        #         i += 1
        #         j += 1
        
        # return j == l2
        #第二种
        for i in t:
            if i in s:
                s = s[s.index(i)+1:]
            else:
                return False
    
        return True
    
    
    class Solution:
        def findLongestWord(self, s: str, d: List[str]) -> str:
            d.sort()
            res = ''
            if d:
                for i in d:
                    if (len(i) < len(res) or (len(i)==len(res) and res < i)):
                        continue
                    else:
                        if (isSubStr(s, i)):
                            res = i
            
            return res
    

    另外一种方法,先将d进行按照len进行降序排序,每一个元素那么最长的排最前面,短的后面~同样长的,就按照字典顺序:a<b<c<d这种排列。所以它不需要遍历d种所有元素

  • 相关阅读:
    UML总结4---UML九种图关系说明
    TCP/IP学习笔记__mbuf
    操作系统内存管理之 内部碎片vs外部碎片
    校园招聘面试-操作系统知识总结 分看点三
    操作系统常见面试题总结 分看点二
    操作系统之面试常考 分看点一
    操作系统基础知识总结(二)
    操作系统基础知识总结(一)
    Java HashMap的扩容
    linux查看端口被占用情况
  • 原文地址:https://www.cnblogs.com/ivan-blog/p/12325924.html
Copyright © 2011-2022 走看看