Leetcode刷题2

一、双指针

80. 删除有序数组中的重复项 II

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。 不需要考虑数组中超出新长度后面的元素。

class Solution(object):
    def removeDuplicates(self, nums):
        slow = 0
        for fast in range(len(nums)):
            if slow < 2 or nums[fast] != nums[slow - 2]:
                nums[slow] = nums[fast]
                slow += 1
        return slow
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        # 变动1: 由于元素可以重复2次,left现在从第二个元素开始,right从第三个元素开始
        left = 1
        for right in range(2, len(nums)):
            # 变动2: 以前之和nums[left]比, 现在还要和nums[left - 1]比,从而保证元素可以重复两次
            if nums[right] == nums[left] and nums[right] == nums[left - 1]:
                continue
            left += 1
            nums[left] = nums[right]
        return left + 1
680. 验证回文串 II

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

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

输入:s = "abca" 输出:true

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

class Solution(object):
    def validPalindrome(self, s):
        isPalindrome = lambda x : x == x[::-1]  #  lambda函数,作用是检查给定字符串是否是回文字符串。
        left, right = 0, len(s) - 1
        while left <= right:
            if s[left] == s[right]:
                left += 1
                right -= 1
            else:
                return isPalindrome(s[left + 1 : right + 1]) or isPalindrome(s[left: right])
        return True

几数之和的问题,可以直接套循环加双指针,如三数之和,就一层循环加双指针,四数之和就是两层循环加双指针,以此类推。

二、脑筋急转弯

319. 灯泡开关

初始时有 n 个灯泡处于关闭状态。第一轮,你将会打开所有灯泡。接下来的第二轮,你将会每两个灯泡关闭第二个。

第三轮,你每三个灯泡就切换第三个灯泡的开关(即,打开变关闭,关闭变打开)。第 i 轮,你每 i 个灯泡就切换第 i 个灯泡的开关。直到第 n 轮,你只需要切换最后一个灯泡的开关。

找出并返回 n 轮后有多少个亮着的灯泡。

输入:n = 3                     输出:1 
解释:初始时, 灯泡状态 [关闭, 关闭, 关闭].
第一轮后, 灯泡状态 [开启, 开启, 开启].
第二轮后, 灯泡状态 [开启, 关闭, 开启].
第三轮后, 灯泡状态 [开启, 关闭, 关闭]. 

你应该返回 1,因为只有一个灯泡还亮着。

给我们 n 个灯泡,总共按 n 次,求最后有多少个亮着的灯泡。

一个灯泡在什么情况下是亮的呢?它如果被按了奇数次就是亮的。

那么一个灯泡 x 最后会被按多少次呢?x 的约数个数就是 x 被按的次数。

一个灯泡是亮着的,等价于它的约数个数为奇数。

所以需要考虑一下,约数个数为奇数的数 有什么性质?

class Solution:
    def bulbSwitch(self, n: int) -> int:
        return int(sqrt(n))
125. 验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

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

输入: s = "A man, a plan, a canal: Panama"                输出:true
解释:"amanaplanacanalpanama" 是回文串。

class Solution:
    def isPalindrome(self, s: str) -> bool:
        s = s.lower()
        new_s = ''
        for i  in s:
            if 'a' <= i <='z' or '0' <=i <='9':
                new_s += i
        return (new_s == new_s[::-1])

三、贪心算法

intervals: List[List[int]])                intervals.sort(key=lambda a: a[1])  右端点从小到大排序

points: List[List[int]])                    points.sort()  左端点从小到大排序 

402. 移掉 K 位数字

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

输入:num = "1432219", k = 3                输出:"1219"

解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。

class Solution(object):
    def removeKdigits(self, num, k):
        stack = []
        remain = len(num) - k
        for digit in num:
            while k and stack and stack[-1] > digit:
                stack.pop()
                k -= 1
            stack.append(digit)
        return ''.join(stack[:remain]).lstrip('0') or '0'

对于最后返回语句的解释

  1. stack[:remain]:使用切片操作符(:)从栈(stack)中取出指定数量(remain)的元素。这里假设栈是一个列表或类似于列表的数据结构。
  2. ''.join(stack[:remain]):使用空字符串作为连接符,将栈中取出的元素拼接成一个字符串。join()方法是字符串的一个方法,用于按照指定的连接符将多个字符串拼接在一起。
  3. .lstrip('0'):使用字符串的lstrip()方法去除字符串开头的连续零。这里的参数是字符串’0’,表示去除开头连续出现的零字符。
  4. or '0':如果经过去除零操作后的字符串为空字符串,使用逻辑运算符or将其替换为字符串’0’。

135. 分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

 输入:ratings = [1,0,2]                输出:5

解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。

先依次给每个小朋友一个糖;

然后先从左到右遍历,如果当前小朋友评分高于左边则糖果在前一个小朋友的糖果数+1;否则糖果数不变;

最后从右到左遍历,依次判断评分,并与左遍历进行比较,取最大糖果数加入结果。(取最大是因为要满足左右两个规则)

class Solution:
    def candy(self, ratings: List[int]) -> int:
        left = [1]*len(ratings)
        right = [1]*len(ratings)

        for i in range(1, len(ratings)):
            if ratings[i] > ratings[i - 1]: 
                left[i] = left[i - 1] + 1
        count = left[-1] # 先记录最后一个小朋友的糖果个数

        for i in range(len(ratings) - 2, -1, -1):
            if ratings[i] > ratings[i + 1]: 
                right[i] = right[i + 1] + 1
            count += max(left[i], right[i])
        return count

四、位运算、数学题、字符串、二分法、滑动窗口

异或

首先针对异或运算,这里做一个知识点的总结:

  1. 任何数和自己做异或运算,结果为 0,即 a ⊕ a = 0。
  2. 任何数和 0 做异或运算,结果还是自己,即 a ⊕ 0 = ⊕。
  3. 异或运算中,满足交换律和结合律,也就是 a ⊕ b ⊕ a = b ⊕ a ⊕ a = b ⊕(a ⊕ a) = b ⊕ 0 = b

异或消除一样的

136. 只出现一次的数字

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # in_list = nums
        return reduce(lambda x, y: x ^ y, nums)

reduce 函数是一个高阶函数,用于在一个可迭代对象上应用一个二进制函数,将其序列的项缩减为单个值

137. 只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法且不使用额外空间来解决此问题。

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        return int((sum(set(nums))*3-sum(nums))//2)

50. Pow(x, n)

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。

class Solution:
    def myPow(self, x: float, n: int) -> float:
        if x == 0.0: return 0.0
        res = 1
        if n < 0: 
            x, n = 1 / x, -n
        while n:
            if n & 1: 
                res *= x
            x *= x
            n >>= 1
        return res

对于一个整数n,n & 1 的结果可以用来判断n的奇偶性。

如果 n & 1 的结果为1,则n为奇数;如果结果为0,则n为偶数。

  1. n&1 (与操作): 判断 nnn 二进制最右一位是否为 111 ;
  2. n>>1n>>1n>>1 (移位操作): nnn 右移一位(可理解为删除最后一位)

二分法 

排序数组中二分

# lower_bound 返回最小的满足 nums[i] >= target 的 i
# 如果数组为空,或者所有数都 < target,则返回 len(nums)
# 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]

# 闭区间写法
def lower_bound(nums: List[int], target: int) -> int:
    left, right = 0, len(nums) - 1  # 闭区间 [left, right]
    while left <= right:  # 区间不为空
        # 循环不变量:
        # nums[left-1] < target
        # nums[right+1] >= target
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid + 1  # 范围缩小到 [mid+1, right]
        else:
            right = mid - 1  # 范围缩小到 [left, mid-1]
    return left  # 或者 right+1

# 左闭右开区间写法
def lower_bound2(nums: List[int], target: int) -> int:
    left, right = 0, len(nums)  # 左闭右开区间 [left, right)
    while left < right:  # 区间不为空
        # 循环不变量:
        # nums[left-1] < target
        # nums[right] >= target
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid + 1  # 范围缩小到 [mid+1, right)
        else:
            right = mid  # 范围缩小到 [left, mid)
    return left  # 或者 right

# 开区间写法
def lower_bound3(nums: List[int], target: int) -> int:
    left, right = -1, len(nums)  # 开区间 (left, right)
    while left + 1 < right:  # 区间不为空
        mid = (left + right) // 2
        # 循环不变量:
        # nums[left] < target
        # nums[right] >= target
        if nums[mid] < target:
            left = mid  # 范围缩小到 (mid, right)
        else:
            right = mid  # 范围缩小到 (left, mid)
    return right  # 或者 left+1

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        i = lower_bound(nums, target)  # 选择其中一种写法即可
        return i if i < len(nums) and nums[i] == target else -1

在二维矩阵中二分

74. 搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵:

  • 每行中的整数从左到右按非递减顺序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。

转换成一维

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        m, n = len(matrix), len(matrix[0])  # 行 列
        i = 0
        j = m*n-1
        while i<=j:
            mid = (j+i)//2
            cur = matrix[mid//n][mid%n]
            if cur == target:
                return True
            if cur > target:
                j = mid-1
            else:
                i = mid+1
        return False

从左下角开始寻找

class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        if matrix == []:
            return False
        r = len(matrix)  # 行
        c = len(matrix[0])  #列
        # 排除只有一行的情况
        if r == 1:
            return target in matrix[0]
        # 排除只有一列的情况
        if c == 1:
            return target in [i for j in matrix for i in j]
        i,j = r - 1,0 # 左下角元素的坐标
        while j <= c-1 and i >= 0:
            if target == matrix[i][j]:
                return True
            elif target < matrix[i][j]:
                i -= 1  # 向上移动
            else:
                j += 1  # 向右移动
        return False     

240. 搜索二维矩阵 II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

 右上角开始寻找:

拿到右上角坐标i,j = 0,c-1 以右上角元素为基数与target做判断

如果matrix[i][j] == target 说明找到了直接return True

如果matrix[i][j] > target 则将其左移 即: j -= 1

如果matrix[j][j] < target 则将其下移 即: i += 1

不越界的条件是: i < r and j >= 0 

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        r = len(matrix) # 行
        c = len(matrix[0]) # 列
        # 排除只有一行的情况
        if r == 1:
            return target in matrix[0]
        # 排除只有一列的情况
        if c == 1:
            return target in [i for j in matrix for i in j]
        # 以右上角元素为起点
        i,j = 0,c-1
        while j >= 0 and i < r:
            if matrix[i][j] == target:
                return True
            if matrix[i][j] > target:
                j -= 1 # 左移
            if matrix[i][j] < target:
                i += 1 # 下移 
        return False

滑动窗口1(可变)

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

输入: s = "abcabcbb"                 ​​​​​输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

滑动窗口+队列

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:
            return 0
        left = 0
        lookup = set()
        max_len = 0
        cur_len = 0
        for i in range(len(s)):
            cur_len += 1
            while s[i] in lookup:
                lookup.remove(s[left])
                left += 1
                cur_len -= 1
            if cur_len > max_len:
                max_len = cur_len
            lookup.add(s[i])
        return max_len

滑动窗口+哈希表

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 定义需要维护的变量, 本题求最大长度,所以需要定义max_len, 该题又涉及去重,因此还需要一个哈希表
        max_len, hashmap = 0, {}

        start = 0
        for end in range(len(s)):
            # 把窗口末端元素加入哈希表,使其频率加1,并且更新最大长度
            hashmap[s[end]] = hashmap.get(s[end], 0) + 1 # 如果get失败就设置为0
            if len(hashmap) == end - start + 1:
                max_len = max(max_len, end - start + 1)
            
            # 当窗口不合法的时候,这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法
            # 当窗口长度大于哈希表长度时候 (说明存在重复元素),窗口不合法
            while end - start + 1 > len(hashmap):
                head = s[start]
                hashmap[head] -= 1
                if hashmap[head] == 0:
                    del hashmap[head]
                start += 1
        return max_len

76. 最小覆盖子串

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

 输入:s = "ADOBECODEBANC", t = "ABC"                 输出:"BANC"

解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        if len(t) > len(s):
            return ''        
        
        cnt = collections.Counter(t)    # 哈希表:记录需要匹配到的各个元素的数目
        need = len(t)                   # 记录需要匹配到的字符总数【need=0表示匹配到了】
        
        start, end = 0, -1          # 记录目标子串s[start, end]的起始和结尾
        min_len = len(s) + 1             # 符合题意的最短子串长度【初始化为一个不可能的较大值】
        left = right = 0            # 滑动窗口的左右边界
        
        for right in range(len(s)):

            if s[right] in cnt:               # 新加入的字符位于t中
                if cnt[s[right]] > 0:         # 对当前字符ch还有需求
                    need -= 1           # 此时新加入窗口中的ch对need有影响
                cnt[s[right]] -= 1
            
            while need == 0:            # need=0,当前窗口完全覆盖了t
                if right - left + 1 < min_len:      # 出现了更短的子串
                    min_len = right - left + 1
                    start, end = left, right
                
                if s[left]  in cnt:           # 刚滑出的字符位于t中
                    if cnt[s[left] ] >= 0:    # 对当前字符ch还有需求,或刚好无需求(其实此时只有=0的情况)
                        need += 1       # 此时滑出窗口的ch会对need有影响
                    cnt[s[left] ] += 1
                left += 1               # 窗口左边界+1
        
        return s[start: end+1]

滑动窗口2(固定)

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7]
解释: 滑动窗口的位置     最大值
       -------------------------     ---------
       [1 3 -1] -3 5 3 6 7             3
       1 [3 -1 -3] 5 3 6 7             3
       1 3 [-1 -3 5] 3 6 7     ​​​​​​​        ​​​​​​​5
       1 3 -1 [-3 5 3] 6 7             5
       1 3 -1 -3 [5 3 6] 7             ​​​​​​​6
       1 3 -1 -3 5 [3 6 7]             7

滑动窗口+队列 

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        ans = []
        q = collections.deque()
        for i in range(n):
            while q and nums[i] >= nums[q[-1]]:
                q.pop()  # 入队前先弹出队尾比待入队的数还小的数
            q.append(i) # 保存索引方便判断是否在窗口内
            while q[0] <= i-k:
                q.popleft()  # 入队后将队首不在窗口内的元素弹出
            if i >= k-1:
                ans.append(nums[q[0]])  # 窗口成形,将队首元素加到结果数组中
        return ans

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值