LeetCode Notes in Python

1 Array & Hashing


217. Contains Duplicate

https://leetcode.com/problems/contains-duplicate/

Approach

Code

class Solution:
    def containsDuplicate(self, nums):
        record = set()
        for i in nums:
            if i in record:
                return True
            record.add(i)
        return False

        # Time complexity: 
        # Space complexity: 

242. Valid Anagram

https://leetcode.com/problems/valid-anagram/

Approach

Code

class Solution:
    def isAnagram(self, s, t):
        if len(s) != len(t):
            return False

        hashmap = collections.defaultdict(list)
        count_s = [0]*26
        count_t = [0]*26

        for l in s:
            count_s[ord(l)-ord("a")] += 1
        for l in t:
            count_t[ord(l)-ord("a")] += 1

        return count_s == count_t

        # Time complexity: 
        # Space complexity: 

1. Two Sum

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def twoSum(self, nums, target):
        record = {}
        for index, value in enumerate(nums):
            diff = target - value
            if diff in record: 
                return record[diff],index
            record[value] = index
        return [] # 如果无解,返回[]

49. Group Anagrams

LeetCode link

Approach

Complexity

  • Time complexity: O(m * n), where m is length of the strs, n is the length of the s
  • Space complexity:

Code

class Solution:
    def groupAnagrams(self, strs):
        res = defaultdict(list)

        for s in strs:
            count = [0]*26
            for c in s:
                count[ord(c)-ord("a")] += 1
            res[tuple(count)].append(s)

        return res.values()

347. Top K Frequent Elements

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def topKFrequent(self, nums, k):
        count = {}
        freq = [[] for i in range(len(nums) + 1)]

        # step1: fill your hashmap
        for n in nums:
            count[n] = 1 + count.get(n, 0)
        for n, c in count.items():
            freq[c].append(n)

        # step2: output via the result array
        res = []
        for i in reversed(range(0, len(freq))):  # go through index reversely
            for n in freq[i]:  # fill the result array
                res.append(n)
                if len(res) == k: # Top K
                    return res

238. Product of Array Except Self

LeetCode link

Approach

  • Brute Force
  • optimization

Complexity

  • Time complexity: O(n)
  • Space complexity: O(1)

Code

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        res = [1] * len(nums)
        # from left to right
        prefix = 1
        for i in range(len(nums)):
            res[i] = prefix
            prefix *= nums[i]
        # from right to left
        postfix = 1
        for i in range(len(nums) - 1, -1, -1):
            res[i] *= postfix
            postfix *= nums[i]
        return res

Two Pointers

125. Valid Palindrome

LeetCode link

Approach

  • initialize l and r
  • while l < r, which is outer loop, controls the two pointers move, point to the next letter.
  • skip constantly until l >= r, which means going through the end of string. Using while rather than if which only make a step forward, due to more than one punctuaction and space.

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def isPalindrome(self, s):
        l, r = 0, len(s) - 1
        while l < r:
            while l < r and not self.alphanum(s[l]): # skip constantly from the left of string until l >= r
                l += 1
            while l < r and not self.alphanum(s[r]): # skip constantly from the right of string until l >= r
                r -= 1
            if s[l].lower() != s[r].lower():
                return False
            l += 1
            r -= 1
        return True

    def alphanum(self, c): # when judgment condition statement is too long to type
        return (
            ord("A") <= ord(c) <= ord("Z")
            or ord("a") <= ord(c) <= ord("z")
            or ord("0") <= ord(c) <= ord("9")
        )

167. Two Sum II - Input Array Is Sorted

LeetCode link

Approach

  • Given a sorted array --> Two pointers

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def twoSum(self, numbers, target):
        l, r = 0, len(numbers) - 1
        while l < r:
            if numbers[l] + numbers[r] == target:
                return [l + 1,r + 1]
            elif numbers[l] + numbers[r] > target:
                r -= 1
            else:
                l += 1
        return -1

15. 3Sum

LeetCode link

Approach

image

  • 首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

  • 依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。

  • 接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

  • 如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def threeSum(self, nums):
        res = []
        nums.sort()

        for i, a in enumerate(nums):
            if a > 0: # return [] if the first element of 3 sum is positive
                break
            if i > 0 and nums[i] == nums[i - 1]: # skip the same i, from i == 1
                continue

            l, r = i + 1, len(nums) - 1
            while l < r:
                if a + nums[l] + nums[r] > 0:
                    r -= 1
                elif a + nums[l] + nums[r] < 0:
                    l += 1
                else:
                    res.append([a, nums[l], nums[r]]) # fill in the result array

					# skip the same nums[r] and nums[l] towards left and right
                    while l < r and nums[r] == nums[r - 1]:
                        r -= 1
                    while l < r and nums[l] == nums[l + 1]:
                        l += 1

                    r -= 1
                    l += 1
        return res

11. Container With Most Water

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def maxArea(self, height):
        l, r = 0, len(height) - 1
        res = 0

        while l < r:
            res = max(res, min(height[l], height[r]) * (r - l))

            if height[l] < height[r]:
                l += 1
            else:
                r -= 1

        return res

Sliding Window

Stack

LeetCode link

Approach

Complexity

  • Time complexity: O(log(n))
  • Space complexity: O(1)

Code

class Solution:
    def search(self, nums, target):
        l = 0
        r = len(nums)

        while l < r:
            mid = l + (r - l) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                l = mid + 1
            elif nums[mid] > target:
                r = mid
        return -1

74. Search a 2D Matrix

LeetCode link

Approach

  • Convert 2D into 1D
  • mid = l + (r - l) // 2 --- 1D
    cur = matrix[mid // n][mid % n] --- 2D

Complexity

  • Time complexity: O(log(m * n))
  • Space complexity: O(1)

Code

class Solution:
    def searchMatrix(self, matrix, target):
        import numpy as np
        m, n = np.shape(matrix)[0], np.shape(matrix)[1]
        l, r = 0, m * n - 1

        while l <= r:
            mid = l + (r - l) // 2
            cur = matrix[mid // n][mid % n]

            if cur == target:
                return True
            elif cur > target:
                r = mid - 1
            else:
                l = mid + 1

        return False

875. Koko Eating Bananas

LeetCode link

Approach

Code

class Solution:
    def minEatingSpeed(self, piles, h):
        l = 1
        r = max(piles)

        while l <= r:
            mid = l + (r - l) // 2
            total_hours = 0
            for p in piles:
                total_hours += math.ceil(p / mid)
            if total_hours > h: # increase the speed
                l = mid + 1
            else: # if meet the requirement, find the minimum speed
                r = mid - 1
        
        return l # the reason that return l not mid is the question's object which is the minimum eating speed (sounds like sliding window)

153. Find Minimum in Rotated Sorted Array

LeetCode link

Approach

Code

class Solution:
    def findMin(self, nums):
        l = 0
        r = len(nums) - 1
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] < nums[r]: # min has not passed the mid
                r = mid
            else: # min has passed the mid
                l = mid + 1
        return nums[r]

33. Search in Rotated Sorted Array

LeetCode link

Approach

  • Binary Search is only useful for the sorted array.
  • mid can partition the nums into two sides, one of two must be sorted.
  • How to verify the list is sorted?
    • It can be sure that there must be one side is sorted.
    • if nums[l] <= nums[mid], then the left side is sorted, another side is unsorted.
    • else, the situation is reversed.

image

Complexity

  • Time complexity: O(logn)
  • Space complexity: O(1)

Code

class Solution:
    def search(self, nums, target):
        l, r = 0, len(nums) - 1

        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] == target:
                return mid
            # the left side partitioned by mid is sorted
            if nums[l] <= nums[mid]:
                # 不能写成target <= nums[mid - 1],mid = 0时,mid - 1 = -1
                # in the sorted left side
                if nums[l] <= target < nums[mid]:
                    r = mid - 1
                # in the unsorted right side
                else:
                    l = mid + 1

            # the right side partitioned by mid is sorted
            else:
                # in the sorted right side
                if nums[mid + 1] <= target <= nums[r]:
                    l = mid + 1
                # in the unsorted left side
                else:
                    r = mid - 1
        return -1

Linked List

Trees

Tries

Heap / Priority Queue

Backtracking

77.Combinations

LeetCode link

Approach

image

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def combine(self, n, k):
        result = []
        path = []
        self.backtracking(k, n, 1, path, result)
        return result

    def backtracking(self, k, n, startindex, path, result):
        # promising function
        if len(path) == k:
            result.append(path[:])
            return
        # single layer search logic
        for i in range(startindex, n + 1):
            path.append(i) 
            self.backtracking(k, n, i + 1, path, result) # recursion
            path.pop() # backtracking

40. Combination Sum II

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def combinationSum2(self, candidates, target):
        result = []
        path = []
        used = [False] * len(candidates)
        startindex = 0
        candidates.sort()
        self.backtracking(startindex, candidates, target, used, path, result)

        return result

    def backtracking(self, startindex, candidates, target, used, path, result):
        if target == 0:
            result.append(path[:])
            return
        if target < 0:
            return
        for i in range(startindex, len(candidates)):
            if target - candidates[i] < 0:
                break
            if i > startindex and candidates[i] == candidates[i - 1] and used[i - 1] == False:
                continue
            
            used[i] = True
            path.append(candidates[i])
            self.backtracking(i + 1, candidates, target - candidates[i], used, path, result)
            path.pop()
            used[i] = False

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code


216. Combination Sum III

LeetCode link

Approach

image

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def combinationSum3(self, k, n):
        result = []
        path =[]
        self.backtracking(k, n, 1, path, result)
        return result

    def backtracking(self, k, n, startindex, path, result):
        # promisong function
        if len(path) == k and sum(path) == n:
            result.append(path[:])
        # single layer search logic
        for i in range(startindex, 10):
            path.append(i)
            self.backtracking(k, n, i + 1, path, result)
            path.pop()

17. Letter Combinations of a Phone Number

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def __init__(self):
        self.letterMap = [
            "",     # 0
            "",     # 1
            "abc",  # 2
            "def",  # 3
            "ghi",  # 4
            "jkl",  # 5
            "mno",  # 6
            "pqrs", # 7
            "tuv",  # 8
            "wxyz"  # 9
        ]
        self.result = []
        self.s = ""

    def backtracking(self, digits, index):
        if index == len(digits):
            self.result.append(self.s)
            return
        digit = int(digits[index])    # 将索引处的数字转换为整数
        letters = self.letterMap[digit]    # 获取对应的字符集
        for i in range(len(letters)):
            self.s += letters[i]    # 处理字符
            self.backtracking(digits, index + 1)    # 递归调用,注意索引加1,处理下一个数字
            self.s = self.s[:-1]    # 回溯,删除最后添加的字符
    
    def letterCombinations(self, digits):
        if len(digits) == 0:
            return self.result
        self.backtracking(digits, 0)
        return self.result

78. Subsets

LeetCode link

Approach

image

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def subsets(self, nums):
        res = []
        path = []
        nums.sort()
        self.backtracking(nums, 0, path, res)
        return res

    def backtracking(self, nums, startindex, path, res):
        res.append(path[:])
        for i in range(startindex, len(nums)):
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, res)
            path.pop()

90. Subsets II

LeetCode link

Approach

image

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def subsetsWithDup(self, nums):
        res = []
        path = []
        nums.sort()
        self.backtracking(nums, 0, path, res)
        return res

    def backtracking(self, nums, startindex, path, res):
        uset = set() # define uset set
        res.append(path[:])
        for i in range(startindex, len(nums)):
            if nums[i] in uset: # skip the same subset
                continue
            uset.add(nums[i]) # fill in uset set
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, res)
            path.pop()

39. Combination Sums

LeetCode linlk

Approach

纵向为递归(backtracking),横向为迭代(for循环)

image

图1 - Backtracking树形图

image
图2 - 简洁结果图

image

图3 - Backtracking动态流程图

Backtracking three elements

  • Backtracking function parameters:
    • 排序之后的candidates数组,targetstartIndexpath数组,res数组
  • Recursion end condition:
    • target == 0path[:]记入res数组
    • target - candidates[i] < 0,回溯(退出该层)
  • Single-layer search condition:
    • 设置startIndex

Complexity

  • Time complexity:
  • Space complexity:

NeetCode 150

III. LeetCode Problem List

二分查找

移除元素

滑动窗口

螺旋矩阵

Solutions

704. 二分查找

力扣题目链接

思路

二分查找法Binary Search

  • 使用条件:有序数组,无重复元素
  • 两种写法:左闭右闭写法、左闭右开写法
  • 边界更新原则:使上一个的middle不在更新后的[left, right]内或[left, right)内
  • 终止条件原则:[1,1]等价于[1],但是[1,1)无意义
    image

image

image

image

代码

# Binary Search 左闭右闭写法 [left, right]
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)-1 
        while left <= right: #[1, 1]是合法的,相当于[1]
            middle = left + (right-left) // 2 
            # (left + right) // 2 若left+right过大,可能会溢出
            if nums[middle] > target:
                right = middle - 1
            elif nums[middle] < target:
                left = middle + 1
            else: 
                return middle
        return -1

# Binary Search 左闭右开写法 [left, right)
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)
        while left < right: #[1, 1]是合法的,相当于[1]
            middle = left + (right-left) // 2 
            # (left + right) // 2 若left+right过大,可能会溢出
            if nums[middle] > target:
                right = middle
            elif nums[middle] < target:
                left = middle + 1
            else: 
                return middle
        return -1

35. 搜索插入位置

力扣题目链接

思路

代码


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

力扣题目链接

思路

代码


69. x的平方根

力扣题目链接

思路

代码


367. 有效完全平方数

力扣题目链接

思路

代码


27. 移除元素

力扣题目链接

思路

数组的元素是不能删的,只能覆盖。
此题可使用快慢指针提高效率

  • 快指针:向右寻找新元素
  • 慢指针:慢慢覆盖更新数组
    image

代码

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        fast = 0 # 快指针
        slow = 0 # 慢指针
        while fast < len(nums):
		# 如果fast寻找的元素不等于val, 则slow照抄元素, slow += 1, fast += 1
		# 如果fast寻找的元素等于val, 则跳过slow, fast += 1
            if nums[fast] != val:
                nums[slow] = nums[fast]
                slow += 1
            fast += 1
        return slow

26. 删除排序数组中的重复项

力扣题目链接

思路

代码


283. 移动零

力扣题目链接

思路

代码


844. 比较含退格的字符串

力扣题目链接

思路

代码


977. 有序数组的平方

力扣题目链接

思路

image

代码

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 将数组元素平方
        for i in range(len(nums)):
            nums[i] = nums[i]**2
        # 初始化
        left = 0
        right = len(nums) - 1
        a = len(nums) - 1 # 结果数组下标,从右向左
        result = [-1]*len(nums) # 结果数组
        # 开始遍历
        while left <= right:
            if nums[left] > nums[left]:
                result[a] = nums[left]
                left = left + 1
            else:
               result[a] = nums[right]
                right = right - 1
            a = a - 1
        return result

209. 长度最小的子数组

力扣题目链接

思路

代码


59. 螺旋矩阵II

力扣题目链接

思路

代码


Table of Contents

Solutions

203. 移除链表元素

力扣题目链接

思路

代码

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        # 设置虚拟头节点
        dummy_head = ListNode(next = head)
        pre = dummy_head
        while pre.next:
            if pre.next.val == val:
                pre.next = pre.next.next # pre指针的next域指向下下个节点,即跳过一个节点
            else:
                pre = pre.next # 头节点向右移动
        return dummy_head.next

707. 设计链表

力扣题目链接

思路

代码

# 单链表
class ListNode:
    def __init__(self, val = 0, next = None): # 初始化链表节点
        self.val = val
        self.next = next

class MyLinkedList:
    def __init__(self): # 初始化
        self.head = ListNode(0) # 虚拟头节点
        self.size = 0 # 设置一个链表长度的属性,便于后续操作,注意每次增和删的时候都要更新

    def get(self, index: int) -> int:
        # while (a)表示:当a!=0时执行循环,当a==0时跳出循环
        if index < 0 or index > self.size - 1: # step1: 判断输入参数是否有效
            return -1
        cur = self.head.next # 第一个节点(头节点的下一个节点)
        while (index): # step2:搜索index所在位置
            cur = cur.next 
            index -= 1 # index递减,直到为0时到达相应位置
        return cur.val

    def addAtHead(self, val: int) -> None:
        pre = self.head
        new_node = ListNode(val)
        new_node.next = pre.next
        pre.next = new_node
        self.size += 1

    def addAtTail(self, val: int) -> None:
        pre = self.head
        new_node = ListNode(val)
        while (pre.next):
            pre = pre.next
        new_node.next = pre.next
        pre.next = new_node
        self.size += 1

    def addAtIndex(self, index: int, val: int) -> None:
        if index < 0:
            self.addAtHead(val)
            return
        elif index == self.size:
            self.addAtTail(val)
            return
        elif index > self.size:
            return

        new_node = ListNode(val)
        pre = self.head
        while (index):
            pre = pre.next
            index -= 1
        new_node.next = pre.next
        pre.next = new_node
        self.size += 1

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index > self.size - 1:
            return
        pre = self.head
        while (index):
            pre = pre.next
            index -= 1
        pre.next = pre.next.next
        self.size -= 1
        


# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)

206. 反转链表

力扣题目链接

思路

image

代码

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

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        # 初始化指针
        pre = None
        cur = head
        # 开始遍历,终止条件为cur指向最右端的下一个空节点None
        while cur:
            temp = cur.next # 存放当前cur.next
            cur.next = pre  # 元素2指向元素1
            pre = cur # pre向右移动一位
            cur = temp # cur向右移动一位
        return pre

24. 两两交换链表中的节点

力扣题目链接

思路

代码


19. 删除链表的倒数第N个节点

力扣题目链接

思路

代码


160. 相交链表

力扣题目链接

思路

代码


142. 环形链表II

力扣题目链接

思路

代码


Table of Contents

Solutions

242. 有效的字母异位词

力扣题目链接

思路

代码

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        record = [0]*26
        for i in s:
            record[ord(i)-ord('a')] += 1
        for j in t:
            record[ord(j)-ord('a')] -= 1
        return record == [0]*26

349. 两个数组的交集

力扣题目链接

思路

代码

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 要点:python利用字典Dictionary实现集合HashSet
        # step1: 初始化空字典和空结果列表
        val_dict = {}
        ans = []
        # step2: 
        # 遍历nums1,出现就记为1,[1,2,2,1]
        # nums1 = [1,2,2,1], val_dict = {'1':1, '2':1}
        for num in nums1:
            val_dict[num] = 1
        # val_dict与nums2比较,
        # 如果key值能在nums2中找到,且value值为1,ans.append(num),相应的value值归0
        # ans = [1,2]
        for num in nums2:
            if num in val_dict.keys() and val_dict[num] == 1:
                ans.append(num)
                val_dict[num] = 0
        
        return ans

202. 快乐数

力扣题目链接

思路

代码

class Solution:
    def isHappy(self, n: int) -> bool:
        def calculate(num: int) -> int:
            sum = 0
            # 82 % 10 = 2,8 % 10 = 8
            while num:
                sum += (num % 10) ** 2 # %表示取模,返回余数
                num = num // 10 # //表示向下取整,返回整数
            return sum
        
        # 记录中间结果
        record = set()

        while True:
            n = calculate(n)
            if n == 1:
                return True

            # 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
            if n in record:
                return False
            else:
                record.add(n)

1. 两数之和

力扣题目链接

思路

代码

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        record = {}
        for index,number in enumerate(nums):
            if target - number in record:
                return [index,record[target - number]]
            else:
                record[number] = index

454. 四数相加II

力扣题目链接

思路

代码


383.赎金信

力扣题目链接

思路

代码


15. 三数之和

力扣题目链接

思路

代码

'''
step1: 排序,为双指针法作准备并进行第一道去重,如[-1,0,1,-1]
step2: 
'''


class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        result = []
        nums.sort() # 排序,为双指针法作准备并进行第一道去重,如[-1,0,1,-1]
        for i in range(len(nums)-2):
            left = i + 1
            right = len(nums) - 1
            # 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if nums[i] > 0: 
                break
            if i >= 1 and nums[i] == nums[i - 1]: # 去重a,注意是nums[i] == nums[i-1]
                continue
            while left < right:
                if nums[i] + nums[left] + nums[right] > 0:
                    right -= 1
                elif nums[i] + nums[left] + nums[right] < 0:
                    left += 1
                else:
                    result.append([nums[i],nums[left],nums[right]])
                    while left < right and nums[left] == nums[left+1]: left += 1
                    while left < right and nums[right] == nums[right-1]: right -= 1
                    left += 1
                    right -= 1
        return result

18. 四数之和

力扣题目链接

思路

代码


Table of Contents

双指针

KMP

Solutions

344. 反转字符串

力扣题目链接

思路

代码


541. 反转字符串II

力扣题目链接

思路

代码


剑指Offer05. 替换空格

力扣题目链接

思路

代码

class Solution:
    def replaceSpace(self, s: str) -> str:
        # 统计空格数量
        count = s.count(' ')
        # 将字符串转换为数组方便操作,扩大数组大小
        result = list(s)
        result.extend([' '] * count * 2)
        # 初始化左右指针
        left = len(s) - 1
        right = len(result) - 1
        # 双指针开始从后向前移动
        while left >= 0:
            if result[left] != ' ':
                result[right] = result[left]
                right -= 1
            else:
                result[right-2:right+1] = '%20'
                right -= 3
            left -= 1
        return ''.join(result) # 'sep'.join() 用新的分隔符连接任意数量的字符串,返回新的字符串

151. 翻转字符串里的单词

力扣题目链接

思路

代码


剑指Offer58-II. 左旋转字符串

力扣题目链接

思路

代码


28. 实现strStr

力扣题目链接

思路

代码


459. 重复的子字符串

力扣题目链接

思路

代码


Table of Contents

Solutions

232. 用栈实现队列

力扣题目链接

思路

代码


225. 用队列实现栈

力扣题目链接

思路

代码


20. 有效的括号

力扣题目链接

思路

代码


1047. 删除字符串中的所有相邻重复项

力扣题目链接

思路

代码


150. 逆波兰表达式求值

力扣题目链接

思路

代码


239. 滑动窗口最大值

力扣题目链接

思路

代码


347. 前K个高频元素

力扣题目链接

思路

代码


Table of Contents

二叉树的遍历方式

深度优先搜索DFS:前中后序遍历

广度优先搜索BFS:层序遍历

求二叉树的属性

二叉树的修改与构造

求二叉搜索树的属性

二叉树公共祖先问题

二叉搜索树的修改与构造

Solutions

144. 二叉树的前序遍历

力扣题目链接

思路

代码


145. 二叉树的后序遍历

力扣题目链接

思路

代码


94. 二叉树的中序遍历

力扣题目链接

思路

代码


102. 二叉树的层序遍历

力扣题目链接

思路

代码

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        "二叉树层序遍历之迭代法"
        # 检查根节点是否为空:若为空,返回[]
        if root == None:
            return []
        
        # 从模块collections中导入deque
        from collections import deque

        # 初始化
        q = deque([root]) # 将根节点存入队列
        results = [] # 结果数组

        # 开始迭代
        # 终止条件:队列为空
        while q:
            size = len(q) # 获取长度,控制每一层的弹出数目
            result = [] # 当前层的结果数组,每一层初始化归零
            for _ in range(size):
                cur = q.popleft() # deque.popleft():移除第一个元素并返回它的值
                result.append(cur.val) # 存入当前层的结果数组

                # 每次当前节点弹出队列后,将其左右孩子保存至队列
                # 由于获取了size,不需要考虑当前层会弹出孩子的故障
                if cur.left:
                    q.append(cur.left)
                if cur.right:
                    q.append(cur.right)
            # 将当前层存入结果数组
            # 使用append保留了[],可以做到实现二维数组。(区分append和extend)
            results.append(result)
        return results

107. 二叉树的层次遍历II

力扣题目链接

思路

对上题的结果进行倒序处理

	# 返回结果
        return results[::-1]

代码

class Solution:
    def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        "二叉树层序遍历之迭代法"
        # 检查根节点是否为空:若为空,返回[]
        if root == None:
            return []
        
        # 从模块collections中导入deque
        from collections import deque

        # 初始化
        q = deque([root]) # 将根节点存入队列
        results = [] # 结果数组

        # 开始迭代
        # 终止条件:队列为空
        while q:
            size = len(q) # 获取长度,控制每一层的弹出数目
            result = [] # 当前层的结果数组,每一层初始化归零
            for _ in range(size):
                cur = q.popleft() # deque.popleft():移除第一个元素并返回它的值
                result.append(cur.val) # 存入当前层的结果数组

                # 每次当前节点弹出队列后,将其左右孩子保存至队列
                # 由于获取了size,不需要考虑当前层会弹出孩子的故障
                if cur.left:
                    q.append(cur.left)
                if cur.right:
                    q.append(cur.right)
            # 将当前层存入结果数组
            # 使用append保留了[],可以做到实现二维数组。(区分append和extend)
            results.append(result)
	# 返回结果
        return results[::-1]

199. 二叉树的右视图

力扣题目链接

思路

image

代码

class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        # 检查根节点是否为空:若为空,返回[]
        if root == None:
            return []
        
        # 从模块collections中导入deque
        from collections import deque

        # 初始化
        queue = deque([root]) # 将根节点存入队列
        result = [] # 结果数组

        # 开始遍历
        # 终止条件:队列为空
        while queue:
            size = len(queue) # 获取长度,控制每一层的弹出数目
            cur = queue[-1] # 取当前层的最后一个元素
            result.append(cur.val)
            for _ in range(size):
                cur = queue.popleft() # 弹出当前层的第一个元素并返回它的值
                # 每次当前节点弹出队列后,将其左右孩子保存至队列
                # 由于获取了size,不需要考虑当前层会弹出孩子的故障
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
        # 返回结果
        return result

637. 二叉树的层平均值

力扣题目链接

思路

代码


429. N叉树的层序遍历

力扣题目链接

思路

代码


515. 在每个树行中找最大值

力扣题目链接

思路

代码


116. 填充每个节点的下一个右侧节点指针

力扣题目链接

思路

代码


117. 填充每个节点的下一个右侧节点指针II

力扣题目链接

思路

代码


101. 对称二叉树

力扣题目链接

思路

代码


104. 二叉树的最大深度

力扣题目链接

思路

代码


111. 二叉树的最小深度

力扣题目链接

思路

代码


222. 完全二叉树的节点个数

力扣题目链接

思路

代码


110. 平衡二叉树

力扣题目链接

思路

代码


257. 二叉树的所有路径

力扣题目链接

思路

代码


404. 左叶子之和

力扣题目链接

思路

代码


513. 找树左下角的值

力扣题目链接

思路

代码


112. 路径总和

力扣题目链接

思路

代码


226. 翻转二叉树

力扣题目链接

思路

代码


106. 从中序与后序遍历序列构造二叉树

力扣题目链接

思路

代码


105. 从前序与中序遍历序列构造二叉树

力扣题目链接

思路

代码


654. 最大二叉树

力扣题目链接

思路

代码


617. 合并二叉树

力扣题目链接

思路

代码


700. 二叉搜索树中的搜索

力扣题目链接

思路

代码


98. 验证二叉搜索树

力扣题目链接

思路

代码


530. 二叉搜索树的最小绝对差

力扣题目链接

思路

代码


501. 二叉搜索树中的众数

力扣题目链接

思路

代码


538. 把二叉搜索树转换为累加树

力扣题目链接

思路

代码


236. 二叉树的最近公共祖先

力扣题目链接

思路

代码


235. 二叉搜索树的最近公共祖先

力扣题目链接

思路

代码


701. 二叉搜索树中的插入操作

力扣题目链接

思路

代码


450. 删除二叉搜索树中的节点

力扣题目链接

思路

代码


669. 修剪二叉搜索树

力扣题目链接

思路

代码


108. 将有序数组转换为二叉搜索树

力扣题目链接

思路

代码


Code

class Solution:
    def combinationSum(self, candidates, target):
        res = []
        path = []
        candidates.sort()
        self.backtracking(candidates, target, 0, path, res)
        return res

    def backtracking(self, candidates, target, startIndex, path, res):
        if target == 0:
            res.append(path[:])
            return

        for i in range(startIndex, len(candidates)):
            if target - candidates[i] < 0:
                break
            path.append(candidates[i])
            self.backtracking(candidates, target - candidates[i], i, path, res)
            path.pop()

51. N-Queens

LeetCode link

Approach

image

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def solveNQueens(self, n):
        result = []
        chessboard = ['.' * n for _ in range(n)]

        self.backtracking(n, 0, chessboard, result)
        return [[''.join(row) for row in solution] for solution in result]

    def backtracking(self, n, row, chessboard, result):
        # promising function
        if row == n:
            result.append(chessboard[:])
            return
        # single layer search logic
        for col in range(n):
            if self.is_valid(row, col, chessboard, n):
                chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col + 1:]
                self.backtracking(n, row + 1, chessboard, result)
                chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col + 1:]

    def is_valid(self, row, col, chessboard, n):
        directions = [(dx, dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1] if (dx, dy) != (0,0)]

        for (dx, dy) in directions:
            i, j = row, col
            while 0 <= i < n and 0 <= j < n:
                if chessboard[i][j] == 'Q':
                    return False
                i += dx
                j += dy

        return True

Graphs

Advanced Graphs

1-D Dynamic Programming

70. Climbing Stairs

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

# recursion
class Solution:
    # recursion
    def climbStairs_recursion(self, n):

        if n in {0,1}:
            return 1
        return self.climbStairs_recursion(n - 1) + self.climbStairs_recursion(n - 2)

    # recursion memoization
    def __init__(self):
        self.memo = {}

    def climbStairs_memoization(self, n):
        if n in {0, 1}:
            return 1
        if n in self.memo:
            return self.memo[n]
        else:
            self.memo[n] = self.climbStairs_memoization(n - 1) + self.climbStairs_memoization(n - 2)

        return self.memo[n]

    # iteration tabulation
    def climbStairs_tabulation(self, n):
        dp = [0] * (n + 1)
        dp[1], dp[2] = 1, 2
        for i in range(3, n + 1):
            dp[i] = dp[i - 1] + dp[i - 2]
        return dp[n]

    # iteration space optimization
    def climbStairs_iteration(self, n):
        # 1 2 3 5 8 13 21
        a, b = 1, 2
        for _ in range(3, n + 1):
            a, b = b, a + b
        return b

if __name__ == '__main__':
    # Test case
    n = 7

    # Build the model
    solution = Solution()
    result_recursion = solution.climbStairs_recursion(n)
    result_memoization = solution.climbStairs_memoization(n)
    result_tabulation = solution.climbStairs_tabulation(n)
    result_iteration = solution.climbStairs_iteration(n)

    # Print
    print(f'{result_recursion}(using the recursion)')
    print(f'{result_memoization}(using the memoization(recursion time optimization)')
    print(f'{result_tabulation}(using the iteration tabulation)')
    print(f'{result_iteration}(using the iteration space optimization')

416. Partition Equal Subset Sum

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if sum(nums) % 2 != 0:
            return False
        # initializaiton
        target = sum(nums) // 2
        dp = [0] * (target + 1)
        # 0-1 knapsack
        for num in nums:
            for j in range(target, num - 1, -1):
                dp[j] = max(dp[j], dp[j - num] + num)
 
        return dp[target] == target

1049. Last Stone Weight II

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        # initialization
        total_sum = sum(stones)
        target = total_sum // 2
        dp = [0] * (target + 1)
        # 0/1 knapsack
        for stone in stones:
            for j in range(target, stone - 1, -1):
                dp[j] = max(dp[j], dp[j - stone] + stone)

        return (total_sum - dp[target]) - dp[target]

494. Target Sum

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code


474. Ones and Zeroes

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code


322. Coin Change

LeetCode link

Approach

Complexity

  • Time complexity:
  • Space complexity:

Code

class Solution:
    def coinChange(self, coins, amount):
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0
        for coin in coins:
            for j in range(coin, amount + 1):
                    dp[j] = min(dp[j], dp[j - coin] + 1)
        return dp[amount] if dp[amount] != float('inf') else -1

2-D Dynamic Programming

Greedy

Intervals

Math & Geometry

Bit Manipulation

6 Sort

6.1 Quick Sort

class Solution:
    def quick_sort(self, nums: list[int], left: int, right: int):
        """快速排序"""
        if left >= right:                          # 子数组长度为 1 时终止递归
            return
        pivot = self.partition(nums, left, right)  # 哨兵划分
        self.quick_sort(nums, left, pivot - 1)     # 递归左子数组
        self.quick_sort(nums, pivot + 1, right)    # 递归右子数组

    def partition(self, nums: list[int], left: int, right: int) -> int:
        """哨兵划分"""
        i, j = left, right
        pivot = nums[left]                         # 以 nums[left] 为基准数
        while i < j:
            while i < j and nums[j] >= pivot:      # 从右向左找首个小于基准数的元素
                j -= 1
            while i < j and nums[i] <= pivot:      # 从左向右找首个大于基准数的元素
                i += 1
            nums[i], nums[j] = nums[j], nums[i]    # 元素交换
        nums[i], nums[left] = nums[left], nums[i]  # 将基准数交换至两子数组的分界线
        return i


if __name__ == "__main__":
    # 快速排序
    nums = [2, 4, 1, 0, 3, 5]
    k = 3

    solution = Solution()
    solution.quick_sort(nums, 0, len(nums) - 1)
    print("快速排序完成后 nums =", nums)

LeetCode 215.KthLargest

Solution 1:

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return sorted(nums)[len(nums)-k]
  • Time Complexity \(O(NlogN)\): 其中 \(N\) 为数组元素数量。
  • Space Complexity: 取决于内置排序算法的具体设计。

Solution 2:

class Solution:
    def findKthLargest(self, nums, k):
        def quick_select(nums, k):
            pivot = random.choice(nums)     # 随机选择基准数
            big, equal, small = [], [], []  # 将大于、小于、等于 pivot 的元素划分至 big, small, equal 中
            for num in nums:
                if num > pivot:
                    big.append(num)
                elif num < pivot:
                    small.append(num)
                else:
                    equal.append(num)
            if k <= len(big):               # 第 k 大元素在 big 中,递归划分
                return quick_select(big, k)
            if len(nums) - len(small) < k:  # 第 k 大元素在 small 中,递归划分
                return quick_select(small, k - len(nums) + len(small))
            return pivot                    # 第 k 大元素在 equal 中,直接返回 pivot
        return quick_select(nums, k)


if __name__ == "__main__":
    import random
    nums = [2, 4, 1, 0, 3, 5]
    k = 3

    solution = Solution()
    num = solution.findKthLargest(nums, k)
    print(f"The kth largest number is {num}")
  • Time Complexity \(O(N)\): 其中 \(N\) 为数组元素数量。
    • 对于长度为 \(N\) 的数组执行哨兵划分操作的时间复杂度为 \(N\)
    • 每轮哨兵划分后,向下递归子数组的平均长度为 \(\frac{N}{2}\)
    • 因此平均情况下,哨兵划分操作一共有 \(N+\frac{N}{2}+\frac{N}{4}+...+\frac{N}{N}=\frac{N−\frac{1}{2}}{1−\frac{1}{2}}=2N−1N + \frac{N}{2} + \frac{N}{4} + ... + \frac{N}{N} = \frac{N - \frac{1}{2}}{1 - \frac{1}{2}} = 2N - 1\)(等比数列求和), 复杂度为 \(O(N)\)
  • Space Complexity \(O(N)\): The worst case is n-1 elements are divided into one of the lists(big, equal, small)

6.2 Merge Sort

观察发现,归并排序与二叉树后序遍历的递归顺序是一致的。

  • 后序遍历:先递归左子树,再递归右子树,最后处理根节点。
  • 归并排序:先递归左子数组,再递归右子数组,最后处理合并。
def merge_sort(nums: list[int], left: int, right: int):
    """
    divide stage:
    [9, 7, 5, 6, 4]
    [9, 7, 5],[6, 4]
    [9, 7],[5],[6, 4]
    [9],[7],[5],[6, 4]
    [9],[7],[5],[6],[4]
    merge stage:
    [7, 9],[5],[6],[4]
    [5, 7, 9],[6],[4]
    [5, 7, 9],[4, 6]
    [4, 5, 6, 7, 9]
    """
    # base case: when subarray length is 1, recursion terminates
    if left >= right:
        return

    # divide stage
    mid = (left + right) // 2         # calculate midpoint
    merge_sort(nums, left, mid)       # recursion over left subarray
    merge_sort(nums, mid + 1, right)  # recursion over right subarray

    # merge stage
    merge(nums, left, mid, right)

def merge(nums: list[int], left: int, mid: int, right: int):

    tmp = [0] * (right - left + 1)    # store the merged subarray for update
    i, j = left, mid + 1              # The left subarray interval is [left, mid]; The right subarray interval is [mid+1, right]
    k = 0                             # for tmp array

    while i <= mid and j <= right:    # 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
        if nums[i] <= nums[j]:
            tmp[k] = nums[i]
            i += 1
        else:
            tmp[k] = nums[j]
            j += 1
        k += 1

    while i <= mid:                   # 将左子数组和右子数组的剩余元素复制到临时数组中
        tmp[k] = nums[i]
        i += 1
        k += 1
    while j <= right:
        tmp[k] = nums[j]
        j += 1
        k += 1

    for k in range(0, len(tmp)):      # 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
        nums[left + k] = tmp[k]


if  __name__=='__main__':
    record = [9, 7, 5, 6, 4]

    merge_sort(record, 0, len(record)-1)
    print(record)

剑指Offer51.逆序对

https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solutions/622496/jian-zhi-offer-51-shu-zu-zhong-de-ni-xu-pvn2h/

class Solution:
    def reversePairs(self, record):
        return self.merge_sort(record, 0, len(record) - 1)

    def merge_sort(self, nums, left, right):
        if left >= right:
            return 0

        mid = (left + right) // 2
        cnt_l = self.merge_sort(nums, left, mid)
        cnt_r = self.merge_sort(nums, mid + 1, right)

        cnt_c = self.merge(nums, left, mid, right)

        return cnt_l + cnt_r + cnt_c

    def merge(self, nums, left, mid, right):
        tmp = [0] * (right - left + 1)
        cnt = 0
        i, j, k = left, mid + 1, 0

        while i <= mid and j <= right:
            if nums[i] <= nums[j]:
                tmp[k] = nums[i]
                i += 1
            else:
                tmp[k] = nums[j]
                j += 1
                cnt += (mid - i + 1) # count the reverse pair
            k += 1
        
        while i <= mid:
            tmp[k] = nums[i]
            i += 1
            k += 1
        while j <= right:
            tmp[k] = nums[j]
            j += 1
            k += 1

        for k in range(len(tmp)):
            nums[left + k] = tmp[k]

        return cnt

if __name__ == '__main__':
    record = [9, 7, 5, 6, 4]
    solution = Solution()

    cnt = solution.reversePairs(record)
    print(cnt)
    print(record)
posted @ 2023-09-09 02:16  ForHHeart  阅读(27)  评论(0编辑  收藏  举报