LeetCode刷题 _「剑指 Offer]专项突破版

第01天 整数

剑指 Offer II 001. 整数除法

class Solution:
    # 时间复杂度:O(logn), 空间复杂度:O(1)
    def divideCore(self, dividend, divisor):
        result = 0
        while dividend <= divisor:   # 因为此时被除数和除数都成了负数
            value = divisor
            quotient = 1
            while (value >= -2**30 and dividend <= value + value):
                quotient += quotient
                value += value
            result += quotient
            dividend -= value
        return result

    def divide(self, dividend: int, divisor: int) -> int:
        if dividend == -2**31 and divisor == -1:
            return 2**31 - 1  # 因为负数转化为正数会有溢出
        negative = 2
        if dividend > 0:
            negative -= 1
            dividend = -dividend
        if divisor > 0:
            negative -= 1
            divisor = -divisor
        result = self.divideCore(dividend, divisor)
        if negative == 1:
            return  -result
        else:
            return result

剑指 Offer II 002. 二进制加法

class Solution:
    def addBinary(self, a: str, b: str) -> str:
        result = ""
        i = len(a) - 1
        j = len(b) - 1
        carry = 0
        while i >= 0 or j >= 0:
            digitA = int(a[i]) if i >= 0 else 0  # 使用id(i)可以看出i自增前后的在内存中的位置是变了的
            i -= 1
            digitB = int(b[j]) if j >= 0 else 0
            j -= 1
            sum = digitA + digitB + carry
            carry = 1 if sum >= 2 else 0
            sum = sum - 2 if sum >= 2 else sum
            result += str(sum)
        result += '1' if carry == 1 else ''
        return result[::-1]

剑指 Offer II 003. 前 n 个数字二进制中 1 的个数

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def countBits(self, n: int) -> List[int]:
        result = [0] * (n + 1)  # 利用好i/2计算1中的个数,
        for i in range(1, n + 1):
            result[i] = result[i >> 1] + (1 & i) # i&(i-1)中1的个数仅比i中1的个数少1个
        return result

第02天 整数

剑指 Offer II 004. 只出现一次的数字

class Solution:
    # 时间复杂度:O(n) 空间复杂度:O(1)
    def singleNumber(self, nums: List[int]) -> int:
        counts = [0] * 32
        for num in nums:
            for i in range(32):
                counts[i] += (num >> (31 - i)) & 1   # 表示从左边起num中的第i个位数中是否是1  # python中特殊的是对于负数,右移动补0 
        res = 0
        for i in range(32):
            res  = (res << 1) + counts[i] % 3   
        # 负数,原码y 反码-y-1 补码-y 移码是补码的
        # python中的按位操作和计算机底层的操作是一样的,比如正数和负数都用补码保存,只不过正数的补码是自身,负数的补码是原码取反再加1
        # 而python中的函数却是经过处理的,便于我们自身理解的,这也是为什么称python是高级语言
        # res ^ 0xffffffff 表示对res的按位取反(截取出32位,把符号位的1全部反过来,但是数据位的也反了) 也就是此时是补码的反码(叫移码)
        # ~ res (此时再取反一次就行了叫,就表示真正的补码)
        return res if counts[0] % 3 == 0 else ~(res ^ 0xffffffff) # 最高位如果是1,证明是负数,此时保存的是补码。最高位如果是0,证明是正数,此时保存的是原码。因为Java中的int是32位

剑指 Offer II 005. 单词长度的最大乘积

class Solution:
    # 时间复杂度:O(nk + n^2) 空间复杂度:O(n),
    # 当判断两个字符串是否包含相同字符时,该方法只需要一次位运算,而哈希表方法可能需要26次布尔运算
    def maxProduct(self, words: List[str]) -> int:
        n = len(words)
        flags = [0] * n
        for i in range(n):
            for c in words[i]:
                flags[i] |= 1 << (ord(c) - ord('a')) # 将字符对应的位数标记为1,或运算
        res = 0
        for i in range(n):
            for j in range(i+1, n):
                if flags[i] & flags[j] == 0:  # 位运算判断是否有相同字符
                    prod = len(words[i]) * len(words[j]) 
                    res = max(res, prod)
        return res

剑指 Offer II 006. 排序数组中两个数字之和

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        i, j = 0, len(numbers) - 1
        while i < j and numbers[i] + numbers[j] != target:
            if numbers[i] + numbers[j] < target:
                i += 1
            else:
                j -= 1
        return [i, j]

第03天 数组

剑指 Offer II 007. 数组中和为 0 的三个数

class Solution:
    # 时间复杂度:O(nlogn + n^2), 排序+找 空间复杂度:O(1)
    def twosum(self, nums, i, res):
        j = i + 1
        k = len(nums) - 1
        while j < k:
            if nums[i] + nums[j] + nums[k] == 0:
                res.append([nums[i], nums[j], nums[k]])
                temp = nums[j]
                while (nums[j] == temp and j < k):  # twosum中滤掉重复的数,只需要考虑较小的数就行了
                    j += 1
            elif nums[i] + nums[j] + nums[k] < 0:
                j += 1
            else:
                k -= 1
        
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res = []
        if len(nums) >= 3:
            nums.sort()  # 先排序,变成有序的
            i = 0
            while i < len(nums) - 2:  
                self.twosum(nums, i, res)
                temp = nums[i]
                i += 1
                while nums[i] == temp and i < len(nums) - 1:  # 滤掉重复的数
                    i += 1
        return res

剑指 Offer II 008. 和大于等于 target 的最短子数组

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)。 感觉是一种贪心算法的思想。
    #如果大于target,就左移P1(因为都是正数,所以相当于减少数字,减少和),如果大于target,就右移P2(相当于增加数字,增加和)
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        left, sum = 0, 0
        res = float('inf')
        for right in range(len(nums)):
            sum += nums[right]
            while left <= right and sum >= target:
                res = min(res, right - left + 1)
                sum -= nums[left]
                left += 1
        return res if res != float('inf') else 0

剑指 Offer II 009. 乘积小于 K 的子数组

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
        product = 1
        left, res = 0, 0
        for right in range(len(nums)):
            product *= nums[right]
            while left <= right and product >= k:
                product /= nums[left]
                left += 1
            res += right - left + 1 if right >= left else 0
        return res

第04天 数组

剑指 Offer II 010. 和为 k 的子数组

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def subarraySum(self, nums: List[int], k: int) -> int:
        sumToCount = dict()
        sumToCount.setdefault(0, 1)
        sum, count = 0, 0
        for num in nums:
            sum += num
            count += sumToCount.get(sum - k, 0)
            sumToCount[sum] = sumToCount.get(sum, 0) + 1
        return count

剑指 Offer II 011. 0 和 1 个数相同的子数组

哈希表,累加数组数字求子数组之和

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(n)
    def findMaxLength(self, nums: List[int]) -> int:
        sumToIndex = {
   0:-1}
        sum, maxLength = 0, 0
        for i in range(len(nums)):
            sum += -1 if nums[i] == 0 else 1
            if sum in sumToIndex.keys():
                maxLength = max(maxLength, i - sumToIndex.get(sum)) 
            else: # 只有不存在时才添加,这样可以保证保留的是第一个累积到当前扫描的数字之和
                sumToIndex[sum] = i     
        return maxLength

剑指 Offer II 012. 左右两边子数组的和相等

累加数组数字求数字之和

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def pivotIndex(self, nums: List[int]) -> int:
        total = 0
        for i in range(len(nums)):
            total += nums[i]
        sum = 0
        for i in range(len(nums)):
            sum += nums[i]
            if sum - nums[i] == total - sum: # 当前数字中左边数组之和 是否等于 右边数组之和
                return i
        return -1

剑指 Offer II 013. 二维子矩阵的和

累加数组数字求子数组之和

class NumMatrix:
    # 时间复杂度:O(mn), 空间复杂度:O(mn)
    def __init__(self, matrix: List[List[int]]):
        if len(matrix) == 0 or len(matrix[0]) == 0:
            return 0
        self.sums = [[0] * (len(matrix[0]) + 1) for _ in range(len(matrix)+1)] # 都一个是为了防止求得本身是左上角的矩阵,以防出具ai出具ai
        for i in range(len(matrix)):
            rowSum = 0
            for j in range(len(matrix[0])):
                rowSum += matrix[i][j]
                self.sums[i + 1][j + 1] = self.sums[i][j + 1] + rowSum
 
    # 时间复杂度:O(1)
    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        return self.sums[row2 + 1][col2 + 1] - self.sums[row1][col2 + 1] - self.sums[row2 + 1][col1] + self.sums[row1][col1]

第05天 字符串

剑指 Offer II 014. 字符串中的变位词

哈希表、双指针

class Solution:
    # 时间复杂度:O(m + n), 空间复杂度:O(1)
    def areAllZero(self,counts):
        for count in counts:
            if count != 0:
                return False
        return True

    def checkInclusion(self, s1: str, s2: str) -> bool:
        if len(s2) >= len(s1):
            counts = [0] * 26
            for i in range(len(s1)):
                counts[ord(s1[i]) - ord('a')] += 1
                counts[ord(s2[i]) - ord('a')] -= 1
            if self.areAllZero(counts):
                return True
            for j in range(len(s1), len(s2)):
                counts[ord(s2[j]) - ord('a')] -= 1
                counts[ord(s2[j - len(s1)]) - ord('a')] += 1
                if self.areAllZero(counts):
                    return True
        return False

剑指 Offer II 015. 字符串中的所有变位词

哈希表、双指针

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def areAllZero(self, counts):
        for count in counts:
            if count != 0:
                return False
        return True

    def findAnagrams(self, s1: str, s2: str) -> List[int]:
        indices = []
        if len(s1) >= len(s2):
            counts = [0] * 26
            for i in range(len(s2)):
                counts[ord(s2[i]) - ord('a')] += 1
                counts[ord(s1[i]) - ord('a')] -= 1
            if self.areAllZero(counts):
                indices.append(0)
            for j in range(len(s2), len(s1)):
                counts[ord(s1[j]) - ord('a')] -= 1
                counts[ord(s1[j - len(s2)]) - ord('a')] += 1
                if self.areAllZero(counts):
                    indices.append(j - len(s2) + 1)
        return indices

剑指 Offer II 016. 不含重复字符的最长子字符串

哈希表+双指针,避免多次遍历整个哈希表

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)。避免需要多次遍历整个哈希表
    def lengthOfLongestSubstring(self, s: str) -> int:
        counts = dict()
        longest = 1 if len(s) > 0 else 0
        countDup = 0
        j = -1 
        for i in range(len(s)):
            counts[s[i]] = counts.get(s[i], 0) + 1
            if counts[s[i]] == 2:
                countDup += 1
            while countDup > 0:
                j += 1
                counts[s[j]] -= 1
                if counts[s[j]] == 1:  # 用删除最右边一个字符后,该位置的值变成1判断是不是之前重复的字符,如果不是之前重复的,那会是-1
                    countDup -= 1
            longest = max(i - j, longest)
        return longest

哈希表+字符串 需要多次遍历整个哈希表

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)。需要多次遍历整个哈希表
    def hasGreaterThan1(self, counts):
        for count in counts.values():
            if count > 1:
                return True
        return False

    def lengthOfLongestSubstring(self, s: str) -> int:
        counts = dict()
        longest = 1 if len(s) > 0 else 0
        j = -1 
        for i in range(len(s)):
            counts[s[i]] = counts.get(s[i], 0) + 1
            while self.hasGreaterThan1(counts):
                j += 1
                counts[s[j]] = counts.get(s[j], 0) - 1
            longest = max(i - j, longest)
        return longest

第06天 字符串

剑指 Offer II 017. 含有所有字符的最短字符串

哈希表+双指针

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def minWindow(self, s: str, t: str) -> str:
        charToCount = dict()
        for ch in t:
            charToCount[ch] = charToCount.get(ch, 0) + 1
        count = len(charToCount)
        start = end = minStart = minEnd = 0
        minLength = float('inf')
        while end < len(s) or (count == 0 and end == len(s)):
            if count > 0:
                endCh = s[end]
                if endCh in charToCount.keys():
                    charToCount[endCh] -= 1
                    if charToCount[endCh] == 0:
                        count -= 1
                end += 1
            else:
                if end - start < minLength:
                    minLength = end - start
                    minStart = start
                    minEnd = end
                startCh = s[start]
                if startCh in charToCount.keys():
                    charToCount[startCh] += 1
                    if charToCount.get(startCh) == 1:
                        count += 1
                start += 1
        return s[minStart:minEnd] if minLength < float('inf') else ""

剑指 Offer II 018. 有效的回文

双指针

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def isPalindrome(self, s: str) -> bool:
        i, j = 0, len(s) - 1
        while i < j:
            ch1 = s[i]
            ch2 = s[j]
            if not ch1.isalnum():
                i += 1
            elif not ch2.isalnum():
                j -= 1
            else:
                if ch1.lower() != ch2.lower():
                    return False
                i += 1
                j -= 1
        return True

剑指 Offer II 019. 最多删除一个字符得到回文

双指针

class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def isPalindrome(self, s, start, end):
        while start < end:
            if s[start] != s[end]:
                break
            start += 1
            end -= 1
        return start >= end

    def validPalindrome(self, s: str) -> bool:
        start = 0
        end = len(s) - 1
        for _ in range(len(s) // 2):
            if s[start] != s[end]:
                break
            start += 1
            end -= 1
        return start == len(s) // 2 or self.isPalindrome(s, start, end - 1) or self.isPalindrome(s, start + 1, end)

剑指 Offer II 020. 回文子字符串的个数

双指针,从里向外,考虑奇数和偶数

class Solution:
    # 时间复杂度:O(n^2), 空间复杂度:O(1)
    def countPalindrome(self, s, start, end):
        count = 0
        while start >= 0 and end < len(s) and s[start] == s[end]:
            count += 1
            start -= 1
            end += 1
        return count

    def countSubstrings(self, s: str) -> int:
        count = 0
        for i in range(len(s)):
            count += self.countPalindrome(s, i, i)
            count += self.countPalindrome(s, i, i + 1)
        return count

第07天 链表

剑指 Offer II 021. 删除链表的倒数第 n 个结点

前后双指针

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    # 时间复杂度:O(n),空间复杂度:O(1)
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode()
        dummy.next = head
        front, back = head, dummy
        for i in range(n):
            front = front.next
        while front is not  None:
            front = front.next
            back = back.next
        back.next = back.next.next
        return dummy.next

剑指 Offer II 022. 链表中环的入口节点

前后指针,不需要知道环中节点数目

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

class Solution:
    # 时间复杂度:O(n),空间复杂度:O(1),不需要知道环中节点数目
    def getNodeInloop(self, head):
        if head is None or head.next is None:
            return None
        slow = head.next
        fast = slow.next
        while (slow is not None and fast is not None):
            if slow == fast:
                return slow
            slow = slow.next
            fast = fast.next
            if fast is not None:
                fast = fast.next
        return None

    def detectCycle(self, head: ListNode) -> ListNode:
        inLoop = self.getNodeInloop(head) # 此时inloop的步数是环中节点数目的整数倍
        if (inLoop is None):
            return None
        node = head
        while node != inLoop:  # 慢指针从头开始,快指针从inLoop开始,相遇时一定是环的入口节点
            node = node.next
            inLoop = inLoop.next
        return node

前后指针,需要知道环中节点数目

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

class Solution:
    # 时间复杂度:O(n),空间复杂度:O(1),需要知道环中节点数目
    def getNodeInloop(self, head):
        if head is None or head.next is None:
            return None
        slow = head.next
        fast = slow.next
        while (slow is not None and fast is not None):
            if slow == fast:
                return slow
            slow = slow.next
            fast = fast.next
            if fast is not None:
                fast = fast.next
        return None

    def detectCycle(self, head: ListNode) -> ListNode:
        inLoop = self.getNodeInloop(head)
        if (inLoop is None):
            return None
        loopCount = 1
        n = inLoop
        while n.next != inLoop:
            loopCount += 1
            n = n.next
        fast = head
        for i in range(loopCount):
            fast = fast.next
        slow = head
        while fast!= slow:
            fast = fast.next
            slow = slow.next
        return slow

剑指 Offer II 023. 两个链表的第一个重合节点

快慢指针

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 时间复杂度:O(m + n), 空间复杂度:O(1)
    def Qiulength(self, head):
        count = 0
        while head != None:
            count += 1
            head = head.next
        return count

    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        count1 = self.Qiulength(headA)
        count2 = self.Qiulength(headB)
        delta = abs(count1 - count2)
        longer = headA if count1 > count2 else headB
        shorter = headB if count1 > count2 else headA
        node1 = longer
        for i in range(delta):
            node1 = node1.next
        node2 = shorter
        while node1 != node2:
            node2 = node2.next
            node1 = node1.next
        return node1

第08天 链表

剑指 Offer II 024. 反转链表

三个指针

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    # 时间复杂度:O(n), 空间复杂度:O(1)
    def reverseList(self, head: ListNode) -> ListNode:
        pre = None
        cur = head
        while cur is not None:
            next = cur.next
            cur.next = pre
            pre = cur
            cur = next
        return pre

剑指 Offer II 025. 链表中的两数相加

三个指针,反转链表的推广

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    # 时间复杂度:O(m + n), 空间复杂度:O(1)
    def addReversed(self, head1, head2):
        dummy = ListNode(0)
        sumNode = dummy
        carry = 0
        while head1 is not None or head2 is not None:
            sum = (0 if head1 is None else head1.val) + (0 if head2 is None else head2.val) + carry
            carry = 1 if sum >= 10 else 0
            sum = sum - 10 if sum >= 10 else sum
            newNode = ListNode(sum)
            sumNode.next = newNode
            sumNode = sumNode.next
            head1 = None if head1 is None else head1.next
            head2 = None if head2 is None else head2.next

        if carry > 0:
            sumNode.next = ListNode(carry)
        return dummy.next
            
    def reverseList(self, head):
        pre = None
        cur = head
        while cur is not None:
            next = cur.next
            cur.next = pre
            pre = cur
            cur = next
        return pre

    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        head1 = self.reverseList(l1)
        head2 = self.reverseList(l2)
        reversedHead = self.addReversed(head1, head2)
        return self.reverseList(reversedHead)

剑指 Offer II 026. 重排链表

快慢指针找到中点,反转链表推广(反转链表的一半)

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    # 时间复杂度:O(n),空间复杂度:O(1)
    def link(self, node1, node2, head):
        pre = head
        while node1 is not None and node2 is not None:
            temp = node1.next

            pre.next = node1
            node1.next = node2
            pre = node2

            node1 = temp 
            node2 = node2.next

        if node1 is not None:
            pre.next = node1
    
    def reverseList(self, first):
        pre = None
        cur = first
        while cur is not None:
            next = cur.next
            cur.next = pre
            pre = cur
            cur = next
        return pre

    def reorderList(self, head: ListNode) -> None:
        dummy = ListNode()
        dummy.next = head
        fast = slow = dummy
        while fast is not None and fast.next is not None:
            slow = slow.next
            fast = fast.next.next
        temp = slow.next
        slow.next = None
        self.link(head, self.reverseList(temp), dummy)

第09天 链表

剑指 Offer II 027. 回文链表

快慢指针,反转链表的推广(反转链表的前半段并且跟后半段做比较)

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    # 时间复杂度:O(n),空间复杂度:O(1)
    def isPalindrome(self, head: ListNode) -> bool:
        if head is None or head.next is None:
            return True
        slow = head
        fast = head.next
        while fast.next is not None and fast.next.next is not None:
            fast = fast.next.next
            slow = slow.next
        secondHalf = slow.next
        if fast.next is not None:
            secondHalf = slow.next.next
        slow.next = None
        return self.equals(secondHalf, self.reverseList(head))
    
    def reverseList(self, head):
        pre = None
        cur = head
        while cur is not None:
            next = cur.next
            cur.next = pre
            pre = cur
            cur = next
        return pre

    def equals(self, l1, l2):
        while l1 is not None and l2 is not None:
            if l1.val != l2.val:
                return False
            l1 = l1.next
            l2 = l2.next
        return True

剑指 Offer II 028. 展平多级双向链表

双向链表

"""
# Definition for a Node.
class Node:
    def __init__(self, val, prev, next, child):
        self.val = val
        self.prev = prev
        self.next = next
        self.child = child
"""
class Solution:
    # 时间复杂度:O(n),空间复杂度:O(k)。k是链表的层数
    def flatten(self, head: 'Node') -> 'Node':
        self.flattenGetTail(head)
        return head

    def flattenGetTail(self, head):
        node = head
        tail = None
        while node is not None:
            next = node.next
            if node.child is not None:
                child = node.child
                childTail = self.flattenGetTail(node.child)
                node.child = None
                node.next = child
                child.prev = node
                childTail.next = next
                if next is not None:
                    next.prev = childTail
                tail = childTail
            else:
                tail = node
            node = next
        return tail

剑指 Offer II 029. 排序的循环链表

指针,循环链表

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值