• python3-算法刷题-数组-双指针-更新中


    双指针有两种:1)快慢指针:两个指针向同一个方向前进,一快一慢;2)左右指针:两个指针相向或相背移动

    快慢指针

    【简单】26. 删除有序数组中的重复项

    https://leetcode.cn/problems/remove-duplicates-from-sorted-array
    给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
    输入:nums = [1,1,2]
    输出:2, nums = [1,2,_]
    解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

    思路:使用下标作为指针。

    class Solution:
        def removeDuplicates(self, nums: List[int]) -> int:
            slow = fast = 0
            while fast < len(nums):
                if nums[slow] != nums[fast]:
                    # +1 是为了让[0,slow]都没有重复,修改的是后一个位置
                    slow += 1
                    nums[slow] = nums[fast]
                fast += 1
            # 要求返回修改后数组的长度 
            return slow + 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    【简单】83. 删除排序链表中的重复元素

    给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
    https://leetcode.cn/problems/remove-duplicates-from-sorted-list/
    思路:跟数组这个完全一样!!!

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
            if head == None or head.next == None: 
                return head
            fast = slow = head
            while fast != None:
                if fast.val != slow.val:
                    slow.next = fast
                    slow = slow.next
                fast = fast.next
            # 此时已经slow后面是重复元素了,所以给它连到None上去
            # 比如1→2→2,slow最后停在第一个2上
            slow.next = None
            return head
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    【简单】27. 移除元素

    https://leetcode.cn/problems/remove-element
    给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
    不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

    思路:双指针。快指针一路向前,如果是val,那么直接跳过去;如果不是val,将快指针值赋值到慢指针位置,然后移动慢指针。

    class Solution:
        def removeElement(self, nums: List[int], val: int) -> int:
            if len(nums) == 0: return 0
            fast = slow = 0
            while fast < len(nums):
                if nums[fast] != val:
                    nums[slow] = nums[fast]
                    slow += 1
                fast += 1    
            return slow
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    【简单】283. 移动零

    https://leetcode.cn/problems/move-zeroes/
    给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
    请注意 ,必须在不复制数组的情况下原地对数组进行操作。

    思路:这题直接复用上面的代码,使val=0即可。然后再将slow后面的元素设为0

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            if len(nums) == 1: return
            fast = slow = 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    或者直接交换也行,这样跟前面的格式更像:

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            fast = slow = 0
            while fast < len(nums):
                if nums[fast] != 0:
                    nums[slow], nums[fast] = nums[fast], nums[slow]
                    slow += 1
                fast += 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    左右指针

    二分查找:基础版

    def binarySearch(nums:list[int], target:int)->int:
        left = 0
        right = len(nums) - 1
        # 注意这里有个=
        while left <=right:
            mid = (right + left) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return -1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    其中计算mid可以换成: left+(right-left)//2 这样可以防止数值过大时溢出

    【中等】167. 两数之和 II - 输入有序数组

    https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/
    在这里插入图片描述
    思路:看到非递减、有序之类的字眼立刻想到能不能用二分查找。。

    class Solution:
        def twoSum(self, numbers: List[int], target: int) -> List[int]:
            left = 0
            right = len(numbers) - 1
            # 不能重复使用
            while left < right:
                s = numbers[left] + numbers[right]
                if s == target:
                    return [left + 1, right + 1]
                elif s < target:
                    left += 1
                else:
                    right -= 1   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    就地反转数组or字符串,判断回文串都是利用相向的双指针。不再赘述。

    二分查找-找到左边界版

    def binarySearchLeftBound(nums:list[int], target:int)->int:
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] == target:
                right = mid - 1 # 不断向左收缩,找左边界
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        if left == len(nums): return -1
        if nums[left] == target: return left
        else: return -1 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    二分查找-找到右边界版

    def binarySearchLeftBound(nums:list[int], target:int)->int:
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] <= target:
                left = mid + 1 # 不断向右
            else:
                right = mid - 1
        if left -1 < 0: return -1
        if nums[left-1] == target: return left-1
        else: return -1 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    【简单】35. 搜索插入位置

    https://leetcode.cn/problems/search-insert-position
    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
    请必须使用时间复杂度为 O(log n) 的算法。
    示例 1:
    输入: nums = [1,3,5,6], target = 5
    输出: 2
    nums 为 无重复元素 的 升序 排列数组

    思路:插入位置是左边界

    class Solution:
        def searchInsert(self, nums: List[int], target: int) -> int:
            left = 0
            right = len(nums) - 1
            while left <= right:
                mid = (left + right) // 2
                if nums[mid] == target:
                    return mid
                elif nums[mid] < target:
                    left = mid + 1
                else:
                    right = mid - 1
            return left
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    【简单】69. x 的平方根

    https://leetcode.cn/problems/sqrtx
    给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
    由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
    注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
    示例 1:
    输入:x = 4
    输出:2
    示例 2:
    输入:x = 8
    输出:2
    解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。

    思路:二分查找,找右边界。(找右边界是因为最终跳出的时候有边界比较小!!!这样好记)

    class Solution:
        def mySqrt(self, x: int) -> int:
            left = 1
            right = x
            while left <= right:
                mid = (left + right) // 2
                m = mid * mid
                if m == x: return mid
                elif m < x:
                    left = mid + 1
                else:
                    right = mid - 1
            return right
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    367. 有效的完全平方数

    在这里插入图片描述
    思路:直接套用,在return mid的时候return True,最后return False完事。

    【中等】34. 在排序数组中查找元素的第一个和最后一个位置

    https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/
    给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

    示例 1:
    输入:nums = [5,7,7,8,8,10], target = 8
    输出:[3,4]

    思路:直接调用上面的函数即可。另外加对[]的判断。

    class Solution:
        def searchLeftBound(self, nums: List[int], target: int) -> int:
            if nums == []: return -1
            left = 0
            right = len(nums) - 1
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] >= target:
                    right = mid -1
                else:
                    left = mid + 1
            if left == len(nums) or nums[left] != target: return -1
            return left
    
        def searchRightBound(self, nums: List[int], target: int) -> int:
            if nums == []: return -1
            left = 0
            right = len(nums) - 1
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] <= target:
                    left = mid + 1
                else:
                    right = mid - 1
            if left < 1 or nums[left-1] != target: return -1
            return left-1
    
        def searchRange(self, nums: List[int], target: int) -> List[int]:
            return [self.searchLeftBound(nums, target), self.searchRightBound(nums, target)]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    【简单】剑指 Offer 53 - I. 在排序数组中查找数字 I

    https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof
    统计一个数字在排序数组中出现的次数。
    示例 1:
    输入: nums = [5,7,7,8,8,10], target = 8
    输出: 2
    示例 2:

    思路:还是直接复用二分查找左右边界的函数。这里直接写主函数了。

    	def search(self, nums: List[int], target: int) -> int:
            left = self.searchLeftBound(nums, target)
            # 如果左边界已经找不到,那没必要继续查找右边界了
            if left == -1: return 0
            right = self.searchRightBound(nums, target)
            return right - left + 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    【简单】278. 第一个错误的版本

    https://leetcode.cn/problems/first-bad-version/
    在这里插入图片描述

    # The isBadVersion API is already defined for you.
    # def isBadVersion(version: int) -> bool:
    
    class Solution:
        def firstBadVersion(self, n: int) -> int:
            left = 1
            right = n
            while left < right:
                mid = left + (right - left) // 2
                # 答案在[left, mid]
                if isBadVersion(mid):
                    right = mid
                else:
                    left = mid + 1
            return left 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    852. 山脉数组的峰顶索引

    https://leetcode.cn/problems/peak-index-in-a-mountain-array

    符合下列属性的数组 arr 称为 山脉数组 :

    • arr.length >= 3
    • 存在 i(0 < i < arr.length - 1)使得:
      arr[0] < arr[1] < … arr[i-1] < arr[i]
      arr[i] > arr[i+1] > … > arr[arr.length - 1]
      给你由整数组成的山脉数组 arr ,返回任何满足 arr[0] < arr[1] < … arr[i - 1] < arr[i] > arr[i + 1] > … > arr[arr.length - 1] 的下标 i 。

    示例 1:
    输入:arr = [0,1,0]
    输出:1
    示例 2:
    输入:arr = [0,2,1,0]
    输出:1

    思路:
    跟上一题的思路是一样的。注意left<=right的话可能一直循环。因为一定会有结果,所以left

    class Solution:
        def peakIndexInMountainArray(self, arr: List[int]) -> int:
            left = 0
            right = len(arr) - 1
            while left < right:
                mid = (left + right) // 2
                if arr[mid] > arr[mid + 1]:
                    right = mid
                else:
                    left = mid + 1
            return left
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    162. 寻找峰值

    https://leetcode.cn/problems/find-peak-element

    峰值元素是指其值严格大于左右相邻值的元素。
    给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
    你可以假设 nums[-1] = nums[n] = -∞ 。
    你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

    输入:nums = [1,2,1,3,5,6,4]
    输出:1 或 5
    解释:你的函数可以返回索引 1,其峰值元素为 2;或者返回索引 5, 其峰值元素为 6。

    思路: 跟852的代码可以完全一样:)

    1385. 两个数组间的距离值

    https://leetcode.cn/problems/find-the-distance-value-between-two-arrays

    给你两个整数数组 arr1 , arr2 和一个整数 d ,请你返回两个数组之间的 距离值 。

    「距离值」 定义为符合此距离要求的元素数目:对于元素 arr1[i] ,不存在任何元素 arr2[j] 满足 |arr1[i]-arr2[j]| <= d 。

    示例 1:
    输入:arr1 = [4,5,8], arr2 = [10,9,1,8], d = 2
    输出:2

    解释:
    对于 arr1[0]=4 我们有:
    |4-10|=6 > d=2
    |4-9|=5 > d=2
    |4-1|=3 > d=2
    |4-8|=4 > d=2
    所以 arr1[0]=4 符合距离要求

    对于 arr1[1]=5 我们有:
    |5-10|=5 > d=2
    |5-9|=4 > d=2
    |5-1|=4 > d=2
    |5-8|=3 > d=2
    所以 arr1[1]=5 也符合距离要求

    对于 arr1[2]=8 我们有:
    |8-10|=2 <= d=2
    |8-9|=1 <= d=2
    |8-1|=7 > d=2
    |8-8|=0 <= d=2
    存在距离小于等于 2 的情况,不符合距离要求

    故而只有 arr1[0]=4 和 arr1[1]=5 两个符合距离要求,距离值为 2

    思路: 这题思路没什么,主要是注意条件,尤其是插入位置在数组范围外时。

    class Solution:
        def seek(self, arr, t):
            left = 0
            right = len(arr) - 1
            while left <= right:
                mid = (left + right) // 2
                if arr[mid] == t:
                    return mid
                elif arr[mid] > t:
                    right = mid - 1
                else:
                    left = mid + 1
            return left
    
        def findTheDistanceValue(self, arr1: List[int], arr2: List[int], d: int) -> int:
            arr2.sort()
            ans = 0
            for i in arr1:
                a = self.seek(arr2, i)
                if a < 0 or len(arr2) < 2:
                    if abs(arr2[0] - i) > d:
                        ans += 1
                elif a == 0:
                    if min(abs(arr2[0] - i), abs(arr2[1] - i)) > d:
                        ans += 1
                        continue
                elif a > len(arr2) - 1 or len(arr2) < 2:
                    if abs(arr2[-1] - i) > d:
                        ans += 1
                elif a == len(arr2) - 1:
                    if min(abs(arr2[-1] - i), abs(arr2[-2] - i)) > d:
                        ans += 1
                        continue
                else:
                    if min(abs(arr2[a-1]-i), abs(arr2[a]-i), abs(arr2[a+1]-i)) > d:
                        ans += 1
                        continue
            return ans
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    回文串

    【中等】5. 最长回文子串

    https://leetcode.cn/problems/longest-palindromic-substring
    给你一个字符串 s,找到 s 中最长的回文子串。
    示例 1:
    输入:s = “babad”
    输出:“bab”
    解释:“aba” 同样是符合题意的答案。

    思路:这次是从中心向两侧扩展。相背而行的双指针。

    class Solution:
        # 计算以[left,right]为中心的最长回文子串
        def findWithBound(self, s:str, left:int, right:int) -> str:
            # 下标合法且满足回文要求,则向两侧扩展子串
            while left >= 0 and right < len(s) and s[left] == s[right]:
                left -= 1
                right += 1
            return s[left+1:right]
    
        def longestPalindrome(self, s: str) -> str:
            ans = ""
            for i in range(len(s)):
                # 如果字串长度为奇数,中心就是自己
                odds = self.findWithBound(s, i, i)
                # 如果字符串长度为偶数,那么输入相邻的两个
                evens = self.findWithBound(s, i, i + 1)
                # 选择长度最长的那个更新ans值
                if len(odds) > len(ans):
                    ans = odds
                if len(evens) > len(ans):
                    ans = evens
            return ans  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    【中等】647. 回文子串

    https://leetcode.cn/problems/palindromic-substrings/
    给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
    输入:s = “aaa”
    输出:6
    解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”

    思路:照着上题的改,只不过每次得到回文的时候都+1

    class Solution:
        def findWithBound(self, s:str, left:int, right:int) -> int:
            cnt = 0
            while left >= 0 and right < len(s) and s[left] == s[right]:
                left -= 1
                right += 1
                # 表示此时有一个回文子串
                cnt += 1
            return cnt
    
        def countSubstrings(self, s: str) -> int:
            cnt = 0
            for i in range(len(s)):
                cnt += self.findWithBound(s,i,i) + self.findWithBound(s,i,i+1)
            return cnt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    125. 验证回文串

    https://leetcode.cn/problems/valid-palindrome
    如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。字母和数字都属于字母数字字符。

    给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。

    示例 1:
    输入: s = “A man, a plan, a canal: Panama”
    输出:true
    解释:“amanaplanacanalpanama” 是回文串。
    示例 2:
    输入:s = “race a car”
    输出:false
    解释:“raceacar” 不是回文串。

    思路: 需要注意的一点是,如果左右指针出界了,说明已经没有可以比较的字母数字字符了,就比如倒数第二个case吧,“,;”,这种,自然要判定为True的。

    class Solution:
        def isPalindrome(self, s: str) -> bool:
            t = []
            s = s.lower()
            left = 0
            right = len(s) - 1
            while left <= right:
                while left < len(s) and not s[left].isalnum():
                    left += 1
                while right > -1 and not s[right].isalnum():
                    right -= 1
                if left >= len(s) or right <= 0: return True
                if s[left] != s[right]:
                    return False
                else:
                    left += 1
                    right -= 1
            return True
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    蓝桥杯嵌入式第12届真题(完成) STM32G431
    【LeetCode每日一题】【单调栈】907. 子数组的最小值之和 Java实现
    【JAVA】普通IO数据拷贝次数的问题探讨
    CCF领航计划第一期:英文论文写作
    使用C#窗体绘制动态曲线图
    如何打造一个网络框架模块对接服务器
    元素使用 align-items center 和 overflow auto 之后,部分内容显示不全
    信奥中的数学:唯一分解定理(算术基本定理)
    pdf压缩文件怎么压缩最小?pdf压缩方法汇总
    基因型数据VCF转EXCEL亲测好用
  • 原文地址:https://blog.csdn.net/pxy7896/article/details/127381437