笔记——week15_python

week15

文章目录

Easy:

680. 验证回文串 II

题目描述:

给你一个字符串 s最多 可以从中删除一个字符。

请你判断 s 是否能成为回文字符串:如果能,返回 true ;否则,返回 false

示例 1:

输入:s = "aba"
输出:true
题解:双指针

解题思路:可以利用双指针的方法来判断是否能构成回文字符串。初始化两个指针左和右,分别指向字符串的开头和结尾。在循环中,比较 s[left] 和 s[right]是否符合,如果符合,则将两个指针分别向中间移动一位。如果不符合,则有两种情况:

  1. 可以删除左边的角色,即将左指针右移一位;
  2. 可以删除右边的角色字符,即将向右指针左移一位。

然后继续判断剩余部分是否构成回文串。如果循环结束时没有找到不符合的字符,则说明可以构成回文串。

class Solution:
    def validPalindrome(self, s: str) -> bool:
        def is_palindrome(left, right):
            while left < right:
                if s[left] != s[right]:
                    return False
                left += 1
                right -= 1
            return True
        
        left, right = 0, len(s) - 1  # 左右指针
        while left < right:
            if s[left] != s[right]:
                return is_palindrome(left + 1, right) or is_palindrome(left, right - 1)  # 左删除或右删除
            left += 1
            right -= 1
        return True

88. 合并两个有序数组

题目描述:

给你两个按 非递减顺序 排列的整数数组 nums1nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。

请你 合并 nums2nums1 中,使合并后的数组同样按 非递减顺序 排列。

**注意:**最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
题解:双指针

解题思路:由于 nums1 内存有足够的空间来承载 nums2 中的元素,可以从后往前遍历,逐比较个 nums1 和 nums2 中的元素,然后将增加的元素放到 nums1 中的元素。之前浏览,可以避免在 nums1 中插入元素时的移动操作。

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.
        """
        p1, p2 = m - 1, 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
        while p2 >= 0: # 如果 nums2 中还有剩余元素,将其放入 nums1
            nums1[p] = nums2[p2]
            p2 -= 1
            p -= 1

2682. 找出转圈游戏输家

题目描述:

n 个朋友在玩游戏。这些朋友坐成一个圈,按 顺时针方向1n 编号。从第 i 个朋友的位置开始顺时针移动 1 步会到达第 (i + 1) 个朋友的位置(1 <= i < n),而从第 n 个朋友的位置开始顺时针移动 1 步会回到第 1 个朋友的位置。

游戏规则如下:

1 个朋友接球。

  • 接着,第 1 个朋友将球传给距离他顺时针方向 k 步的朋友。
  • 然后,接球的朋友应该把球传给距离他顺时针方向 2 * k 步的朋友。
  • 接着,接球的朋友应该把球传给距离他顺时针方向 3 * k 步的朋友,以此类推。

换句话说,在第 i 轮中持有球的那位朋友需要将球传递给距离他顺时针方向 i * k 步的朋友。

当某个朋友第 2 次接到球时,游戏结束。

在整场游戏中没有接到过球的朋友是 输家

给你参与游戏的朋友数量 n 和一个整数 k ,请按升序排列返回包含所有输家编号的数组 answer 作为答案。

示例 1:

输入:n = 5, k = 2
输出:[4,5]
解释:以下为游戏进行情况:
1)第 1 个朋友接球,第 1 个朋友将球传给距离他顺时针方向 2 步的玩家 —— 第 3 个朋友。
2)第 3 个朋友将球传给距离他顺时针方向 4 步的玩家 —— 第 2 个朋友。
3)第 2 个朋友将球传给距离他顺时针方向 6 步的玩家 —— 第 3 个朋友。
4)第 3 个朋友接到两次球,游戏结束。
题解:模拟

使用模拟的方式来解决,需要注意的是根据规则进行传递球的过程

class Solution:
    def circularGameLosers(self, n: int, k: int) -> List[int]:
        visit = [False] * n  # 创建一个记录是否被访问过的列表,初始都为 False
        i = k  # 初始化步长
        j = 0  # 初始位置

        while not visit[j]:  # 当位置未被访问过时继续
            visit[j] = True  # 标记当前位置为已访问
            j = (i + j) % n  # 计算下一个位置
            i += k  # 更新步长
        ans = []
        for i in range(n):
            if not visit[i]:
                ans.append(i+1)  # 将未访问过的位置加入答案列表
        return ans

69. x 的平方根

题目描述:

给你一个非负整数 x ,计算并返回 x算术平方根

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

**注意:**不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5

示例 1:

输入:x = 4
输出:2
题解:二分查找

我们可以把这道题想象成,给定一个非负整数a,求 f ( x ) = x 2 − a = 0 f(x)=x^2-a=0 f(x)=x2a=0 的解。因为我们只考虑 x ≥ 0 x\geq0 x0,所以 f ( x ) f(x) f(x) 在定义域上是单调递增的。考虑到 f ( x ) = − a < 0 f(x)=-a<0 f(x)=a<0 f ( x ) = a 2 − a ≥ 0 f(x) = a^2-a\geq0 f(x)=a2a0我们可以对 [ 0 , a ] [0, a] [0,a] 区间使用二分法找到 f ( x ) = 0 f(x)=0 f(x)=0的解。

class Solution:
    def mySqrt(self, x: int) -> int:
        l, r, ans = 0, x, -1  # 初始化左边界 l 为 0,右边界 r 为 x,ans 为初始值 -1
        
        while l <= r:  # 当左边界小于等于右边界时进行循环
            mid = (l + r) // 2  # 计算中间值 mid
            
            if mid * mid <= x:  # 如果 mid 的平方小于等于 x
                ans = mid  # 更新 ans 为当前 mid,因为 mid 可能是更接近平方根的值
                l = mid + 1  # 调整左边界为 mid 的下一个数,继续搜索更大的值
            else:
                r = mid - 1  # 调整右边界为 mid 的前一个数,将搜索范围调整到更小的一半
        
        return ans  # 返回最终的 ans 作为平方根的整数部分
题解:牛顿迭代法

牛顿迭代法——其公式为 x n + 1 = x n − f ( x n ) / f / ( x n ) x_{n+1}=x_n-f(x_n)/f^/(x_n) xn+1=xnf(xn)/f/(xn)。给定 f ( x ) = x 2 − a = 0 f(x) = x^2-a=0 f(x)=x2a=0,这里的迭代公式为 x n + 1 = ( x n + a / x n ) / 2 x_{n+1}=(x_n+a/x_n)/2 xn+1=xn+a/xn)/2

class Solution:
    def mySqrt(self, x: int) -> int:
        if x == 0:
            return 0

        c = float(x)
        x0 = float(x)

        # 迭代计算 x 的平方根。
        while True:
            xi = 0.5 * (x0 + c / x0)
            if abs(x0 - xi) < 1e-7:
                break
            x0 = xi

        return int(x0)

Medium:

633. 平方数之和

题目描述:

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

示例 1:

输入:c = 5
输出:true
解释:1 * 1 + 2 * 2 = 5
题目描述:双指针

双解题思路:这道题可以用指针的方法来解决。初始化一个指针left和right,分别指向0和sqrt©。然后在循环中,计算 l e f t 2 + r i g h t 2 left^2 + right^2 left2+right2的值,如果等于c,则找到了解,返回True。如果小于c,则将向左向右移动一位,使左增加。如果大于c,则将右向左移动一位,使右增加。循环终止条件是left <= right。如果循环结束后仍没有找到解,则返回False。

class Solution:
    def judgeSquareSum(self, c: int) -> bool:
        a, b = 0, int(c ** 0.5)  # 左右数
        while a <= b:
            current_sum = a ** 2 + b ** 2
            if current_sum == c:  # 大小相同
                return True
            elif current_sum < c:  # 过小,左指针右移
                a += 1
            else:  # 过大,右指针左移
                b -= 1
        return False

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

题目描述:

给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。

如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。

示例 1:

输入:s = "abpcplea", dictionary = ["ale","apple","monkey","plea"]
输出:"apple"
题解:双指针

解题思路:检索批量 d 中的每个字符串,然后判断当前字符串是否可以通过删除 s 中的一些字符得到。可以通过双指针来判断两个字符串是否匹配。

class Solution:
    def findLongestWord(self, s: str, dictionary: List[str]) -> str:
        def Astringexists(s, target):
            i = j = 0  # 分别检索s和target
            while i < len(s) and j < len(target):
                if s[i] == target[j]:  # targt指针移动
                    j += 1
                i += 1  # s指针移动
            return j == len(target)  # 满足条件

        # 先长度,在字典序排序
        dictionary.sort(key=lambda x: (-len(x), x))  # 从大到小,所以添加负号

        for word in dictionary:
            if Astringexists(s, word):  # 判断是否是最长
                return word
        return ""

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

题目描述:

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
题解:二分查找

解决方案:

  1. 使用二分查找找到第一个等于 target 的元素的索引。
  2. 使用二分查找找到最后一个等于 target 的元素的索引。
  3. 返回一个包含两个索引的列表。
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        # 两次二分查找,找左右边界
        def searchLeft(nums, target):  # 左
            left, right = 0, len(nums) - 1  # 指针
            while left <= right:
                mid = (right - left) // 2 + left  # 防止溢出
                if nums[mid] == target:  # 找到target
                    if mid == 0 or nums[mid-1] < target:  # 判断是否是左边界
                        return mid
                    else:
                        right = mid - 1  # 不是,右指针左移
                elif nums[mid] > target:  # 过大
                    right = mid - 1
                else:  # 过小
                    left = mid + 1
            return -1
        
        def searchRight(nums, target):  # 右
            left, right = 0 ,len(nums) - 1  # 防止溢出
            while left <= right:  # 找到target
                mid = (right - left) // 2 + left
                if nums[mid] == target:
                    if mid == len(nums) - 1 or nums[mid+1] > target:  # 判断是否是右边界
                        return mid
                    else:
                        left = mid + 1  # 不是,左指针右移
                elif nums[mid] > target:  # 过大
                    right = mid - 1
                else:  # 过小
                    left = mid + 1
            return -1

        return [searchLeft(nums, target), searchRight(nums, target)]

81. 搜索旋转排序数组 II

题目描述:

已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4]

给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false

你必须尽可能减少整个操作步骤。

示例 1:

输入:nums = [2,5,6,0,0,1,2], target = 0
输出:true
题解:二分查找

即使数组被旋转过,我们仍然可以利用这个数组的递增性,使用二分查找。对于当前的中点,如果它指向的值小于等于右端,那么说明右区间是排好序的;反之,那么说明左区间是排好序的。如果目标值位于排好序的区间内,我们可以对这个区间继续二分查找;反之,我们对于另一半区间继续二分查找。
注意,因为数组存在重复数字,如果中点和左端的数字相同,我们并不能确定是左区间全部相同,还是右区间完全相同。在这种情况下,我们可以简单地将左端点右移一位,然后继续进行二分查找。

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        # 二分查找
        left, right = 0, len(nums) - 1   # 左右指针

        while left <= right:  # 开始循环
            mid = (right - left) // 2 + left  # 中间点
            if nums[mid] == target:  # 存在目标数
                return True
            if nums[left] == nums[right]:  # 无法判断那个区间是递增的
                left += 1
            elif nums[mid] <= nums[right]:  # 当前[mid, right]
                if nums[mid] < target and nums[right] >= target:  # 检索右区间
                    left = mid + 1
                else:
                    right = mid - 1
            else:
                if nums[mid] > target and nums[left] <= target:  # 检索左区间
                    right = mid - 1
                else:
                    left = mid + 1
        return False

153. 寻找旋转排序数组中的最小值

题目描述:

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
题解:二分查找

数组分布如下图,中间值在左递增区间,向右靠拢,在右边递增区间,向左靠拢。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KSW4ioLY-1692539210236)(C:\Users\86152\Desktop\1.png)]

class Solution:
    def findMin(self, nums: List[int]) -> int:
        start, end = 0, len(nums) - 1  # 首尾两个指针
        minNum = float('inf')  # 记录最小值
        while start <= end:  # 开始搜索
            mid = (end - start) // 2 + start  # 中值 防止溢出
            if nums[mid] < minNum:
                minNum =  nums[mid]
            if nums[mid] < nums[end]:  # mid落在右半部分的递增区间中 
                end = mid  # 向左边靠拢
            else:  # mid落在左半部分的递增区间中
                start = mid + 1 # 向右边靠拢

        return minNum
class Solution:
    def findMin(self, nums: List[int]) -> int:
        start, end = 0, len(nums) - 1  # 首尾两个指针
        while start < end:  # 开始搜索
            mid = (end - start) // 2 + start  # 中值 防止溢出
            if nums[mid] < nums[end]:  # mid落在右半部分的递增区间中 
                end = mid  # 向左边靠拢
            else:  # mid落在左半部分的递增区间中
                start = mid + 1 # 向右边靠拢

        return nums[start]

540. 有序数组中的单一元素

题目描述:

给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。

请你找出并返回只出现一次的那个数。

你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。

示例 1:

输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2
题解:二分查找

m i d mid mid是偶数时,则比较 n u m s [ m i d ] 和 n u m s [ m i d + 1 ] nums[mid]和nums[mid+1] nums[mid]nums[mid+1]是否相等;

如果 m i d mid mid是奇数,则比较 n u m s [ m i d ] 和 n u m s [ m i d + 1 ] nums[mid]和nums[mid+1] nums[mid]nums[mid+1]是否相等。

m i d mid mid是偶数时, m i d + 1 = m i d ⨁ 1 mid+1=mid\bigoplus1 mid+1=mid1

m i d mid mid是奇数时, m i d − 1 = m i d ⨁ 1 mid-1=mid\bigoplus1 mid1=mid1

mid = 2
mid ^ 1
3
mid = 1
mid ^ 1
0
class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        # 现象 在出现独立数之前和之后,奇偶位数的值发生了变化
        # 偶+奇 --->  奇+偶
        # mid满足那个 就更加靠近那个指针
        low, high = 0, len(nums) - 1  # 左右指针
        while low < high: 
            # 找到中间元素
            mid = (high - low) // 2 + low
            # 如果偶+奇 
            if nums[mid] == nums[mid ^ 1]:  # 合并写法
                low = mid + 1
            # 如果奇+偶
            else:
                high = mid

        # 返回右指针指向的元素
        return nums[high]

Hard:

76. 最小覆盖子串

题目描述:

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
题解:滑动窗口

该算法使用了滑动窗口的思想,通过leftright两个right,直t中的字符。然后通过移动left指针缩小窗口,直到无法再满足条件,然后再继续滑动``right`,循环迭代,最终找到

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        need = {}  # 哈希表统计字符的数量
        for c in t:
            need[c] = need.get(c, 0) + 1  # 键c不存在,返回默认值 0
        left = right = 0  # 滑动窗口的左右指针
        valid_num = 0  # 统计窗口的达到条件的字符数量
        min_stat = 0  # 最小串的起始位置
        min_len = float('inf')  # 最小串的长度
        window = {}   # 记录窗口内字符出现的数量

        while right < len(s):
            c = s[right]
            right += 1  # 为窗口添加字符 
            if c in need:
                window[c] = window.get(c, 0) + 1
                if window[c] == need[c]: # 某个字符达到条件
                    valid_num += 1

            while valid_num == len(need):  # 某一个子串达到条件
                if min_len > right - left:  # 字串更小可以更改数据
                    min_len = right - left
                    min_start = left
                # 开始靠右移动左指针
                d = s[left]  # 当前左指针指向的字符
                left += 1
                if d in need:
                    if window[d] == need[d]:
                        valid_num -= 1
                    window[d] -= 1

        return "" if min_len == float('inf') else s[min_start:min_start + min_len]

154. 寻找旋转排序数组中的最小值 II

题目描述:

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
  • 若旋转 7 次,则可以得到 [0,1,4,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须尽可能减少整个过程的操作步骤。

示例 1:

输入:nums = [1,3,5]
输出:1
解题思路:二分查找

思路还是和81. 搜索旋转排序数组 II差不多的,多的是对相同数的处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DA7gklDv-1692539210241)(C:\Users\86152\Desktop\1.png)]

class Solution:
    def findMin(self, nums: List[int]) -> int:
        low, high = 0, len(nums) - 1  # 左右指针
        while low <= high:  # 开始搜索
            mid = (high - low) // 2 + low
            if nums[low] == nums[high]:
                low += 1
            elif nums[mid] <= nums[high]:
                high = mid
            else:
                low = mid + 1
        return nums[high]
输入:nums = [1,3,5]
输出:1
解题思路:二分查找

思路还是和81. 搜索旋转排序数组 II差不多的,多的是对相同数的处理

[外链图片转存中…(img-DA7gklDv-1692539210241)]

class Solution:
    def findMin(self, nums: List[int]) -> int:
        low, high = 0, len(nums) - 1  # 左右指针
        while low <= high:  # 开始搜索
            mid = (high - low) // 2 + low
            if nums[low] == nums[high]:
                low += 1
            elif nums[mid] <= nums[high]:
                high = mid
            else:
                low = mid + 1
        return nums[high]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值