Breaking Through the Algorithm Maze: 50 Selected Questions-A Guide to Algorithm Questions

Preface

In the fields of computer science and programming, algorithms and data structures are fundamental concepts that you need to master whether you are a beginner or an experienced developer. This article will give you an in-depth understanding of a series of common algorithm and data structure problems, including binary search, sliding window, linked list, binary tree, TopK, design problems, dynamic programming, etc. We'll provide explanations and code examples for each question to help you better understand and apply the concepts.
Personal problem solution GitHub link: LeetCode-Go-Python-Java-C
Part of the content of this article comes from online collection and personal practice. If any information contains errors, readers are welcome to criticize and correct them. This article is only for learning and communication, not for any commercial purposes. Welcome to subscribe to the column, one question every day, and improve the LeetCode column
with the bloggers

Article directory

Related question types

  1. binary search

  2. sliding window

  3. array

  4. linked list

  5. Binary tree

  6. TopK

  7. Design question

  8. dynamic programming

  9. other

  10. series of questions

Basic knowledge review

1. Binary search

Algorithm principle

Binary search is an efficient search algorithm suitable for ordered arrays. The principle is based on the idea of ​​continuously narrowing the search scope. Proceed as follows:

  1. Determine the left leftand right boundaries of the search range right.
  2. Calculate the index of the middle element midand get the value of the middle element mid_value.
  3. mid_valueReturns if is equal to the target value mid.
  4. If mid_valueis larger than the target value, rightupdate to mid - 1, narrowing the search range to the left half.
  5. If mid_valueis smaller than the target value, leftupdate to mid + 1, narrowing the search range to the right half.
  6. Repeat steps 2 to 5 until the target value is found or the search range is empty.
Implementation example

The following is sample code for binary search (Python):

def binary_search(nums, target):
    left, right = 0, len(nums) - 1
    
    while left <= right:
        mid = left + (right - left) // 2
        mid_value = nums[mid]
        
        if mid_value == target:
            return mid
        elif mid_value > target:
            right = mid - 1
        else:
            left = mid + 1
    
    return -1  # 目标值不存在

# 示例用法
nums = [1, 3, 5, 7, 9]
target = 5
result = binary_search(nums, target)
if result != -1:
    print(f"目标值 {
      
      target} 在索引 {
      
      result} 处找到。")
else:
    print("目标值不存在。")

2. Sliding window

Basic concept of sliding window

The sliding window algorithm is an efficient method for dealing with problems of consecutive subarrays or substrings. It maintains a variable-sized window to move through the data stream and perform related operations.

Sliding window maximum problem

The sliding window maximum problem is to find the maximum value of each window in the array given an array and a fixed-size window. This can be achieved with a double-ended queue (deque), keeping the elements in the queue in descending order.

from collections import deque

def max_sliding_window(nums, k):
    result = []
    window = deque()

    for i, num in enumerate(nums):
        # 移除窗口左侧超出窗口大小的元素
        while window and window[0] < i - k + 1:
            window.popleft()

        # 从窗口右侧移除所有小于当前元素的元素
        while window and nums[window[-1]] < num:
            window.pop()

        window.append(i)

        # 当窗口达到大小k时,将最大值加入结果列表
        if i >= k - 1:
            result.append(nums[window[0]])

    return result

# 示例用法
nums = [1, 3, -1, -3, 5, 3, 6, 7]
k = 3
result = max_sliding_window(nums, k)
print(result)  # 输出:[3, 3, 5, 5, 6, 7]
Sliding window median problem

Finding the median in a sliding window requires maintaining an ordered data structure such as a balanced binary search tree (AVL tree) or two heaps (max-heap and min-heap). By splitting the data in half and storing it in two heaps, the median can be found in O(1) time.

import heapq

class MedianFinder:
    def __init__(self):
        self.min_heap = []  # 存储较大一半的元素
        self.max_heap = []  # 存储较小一半的元素

    def addNum(self, num):
        if not self.max_heap or num <= -self.max_heap[0]:
            heapq.heappush(self.max_heap, -num)
        else:
            heapq.heappush(self.min_heap, num)

        # 保持两个堆的大小平衡
        if len(self.max_heap) > len(self.min_heap) + 1:
            heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap))
        elif len(self.min_heap) > len(self.max_heap):
            heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap))

    def findMedian(self):
        if len(self.max_heap) == len(self.min_heap):
            return (-self.max_heap[0] + self.min_heap[0]) / 2.0
        else:
            return -self.max_heap[0]

# 示例用法
median_finder = MedianFinder()
median_finder.addNum(1)
median_finder.addNum(2)
median_finder.addNum(3)
median = median_finder.findMedian()
print(median)  # 输出:2.0

1- Ordered list solution

from sortedcontainers import SortedList

class Solution:
    def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:
        res = []  # 存放结果的列表
        m = SortedList()  # 使用SortedList维护窗口内的元素

        for i, num in enumerate(nums):
            if i >= k:
                m.remove(nums[i - k])  # 移除窗口最左侧的元素

            m.add(num)  # 将新元素添加到窗口

            if i - k + 1 >= 0:
                if not k % 2:
                    # 如果窗口大小为偶数,计算中位数并添加到结果
                    res.append((m[len(m) // 2 - 1] + m[len(m) // 2]) / 2)
                else:
                    # 如果窗口大小为奇数,直接添加中位数到结果
                    res.append(m[len(m) // 2])

        return res
from sortedcontainers import SortedList

class Solution:
    def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:
        result = []
        window = SortedList(nums[:k])  # 使用SortedList维护窗口内的元素

        for i in range(k, len(nums)):
            # 计算中位数并添加到结果集
            median = (window[k // 2] + window[(k - 1) // 2]) / 2.0
            result.append(median)

            window.add(nums[i])  # 将新元素添加到窗口
            window.remove(nums[i - k])  # 移除窗口最左侧的元素

        # 处理最后一个窗口
        median = (window[k // 2] + window[(k - 1) // 2]) / 2.0
        result.append(median)

        return result

2-Heap solution method

import heapq

class Solution:
    def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:
        if k == 1:
            return [float(num) for num in nums]  # 如果窗口大小为1,直接返回每个元素的浮点数形式的列表

        res = []  # 存储中位数的结果列表
        mid_cnt = k // 2  # 窗口的中位数索引

        # 初始化两个堆,info1用于存储较大一半的元素(负值),info2用于存储较小一半的元素
        info1 = [(-num, i) for i, num in enumerate(nums[:mid_cnt])]
        info2 = []

        heapq.heapify(info1)  # 将info1转化为最小堆

        def get_median(info1, info2, k):
            # 辅助函数,计算中位数
            if k % 2 == 0:
                return (-info1[0][0] + info2[0][0]) / 2.0
            else:
                return float(info2[0][0])

        # 初始窗口滑动过程
        for i in range(mid_cnt, k):
            if nums[i] < -info1[0][0]:
                num, j = heapq.heappop(info1)  # 弹出info1的堆顶元素
                heapq.heappush(info2, (-num, j))  # 将弹出的元素加入info2
                heapq.heappush(info1, (-nums[i], i))  # 将新元素加入info1
            else:
                heapq.heappush(info2, (nums[i], i))  # 将新元素加入info2

        res.append(get_median(info1, info2, k))  # 计算并添加初始窗口的中位数

        i = k
        while i < len(nums):
            num, j = nums[i - k], i - k  # 要移出窗口的元素和其索引

            if num <= -info1[0][0]:
                if nums[i] < info2[0][0]:
                    heapq.heappush(info1, (-nums[i], i))  # 新元素加入info1
                else:
                    num1, i1 = info2[0]
                    heapq.heappush(info1, (-num1, i1))  # info2的堆顶元素加入info1
                    heapq.heapreplace(info2, (nums[i], i))  # 弹出info2的堆顶并加入新元素
            else:
                if nums[i] < -info1[0][0]:
                    num1, i1 = info1[0]
                    heapq.heappush(info2, (-num1, i1))  # info1的堆顶元素加入info2
                    heapq.heapreplace(info1, (-nums[i], i))  # 弹出info1的堆顶并加入新元素
                else:
                    heapq.heappush(info2, (nums[i], i))  # 新元素加入info2

            while info1[0][1] <= j:
                heapq.heappop(info1)  # 移出info1中已经不在窗口中的元素
            while info2[0][1] <= j:
                heapq.heappop(info2)  # 移出info2中已经不在窗口中的元素

            res.append(get_median(info1, info2, k))  # 计算并添加当前窗口的中位数
            i += 1  # 窗口右移一位

        return res

Longest substring without repeated characters problem

The longest substring without repeating characters problem is to find the longest substring in a string that does not contain repeating characters. This can be solved by using a sliding window, while using a hash table to record where characters appear.

def length_of_longest_substring(s):
    if not s:
        return 0

    char_index_map = {
    
    }  # 记录字符的最后出现位置
    max_length = 0
    start = 0

    for end in range(len(s)):
        if s[end] in char_index_map and char_index_map[s[end]] >= start:
            start = char_index_map[s[end]] + 1
        char_index_map[s[end]] = end
        max_length = max(max_length, end - start + 1)

    return max_length


# 示例用法
s = "abcabcbb"
length = length_of_longest_substring(s)
print(length)  # 输出:3,对应的最长子字符串是 "abc"

s = "bbbbb"
length = length_of_longest_substring(s)
print(length)  # 输出:1,对应的最长子字符串是 "b"

s = "pwwkew"
length = length_of_longest_substring(s)
print(length)  # 输出:3,对应的最长子字符串是 "wke"

The above is a brief example of the sliding window and longest substring without repeating characters problem. These problems are one of the typical applications of the sliding window algorithm, which is used to solve a variety of string processing problems. Hopefully these examples will help understand the core idea and application of the sliding window algorithm.

3. Array

Merge two sorted arrays

Merging two sorted arrays is a common problem and can be achieved using the double pointer method. First merge the two arrays into a new array and then sort the new array.

def merge_sorted_arrays(nums1, m, nums2, n):
    i, j, k = m - 1, n - 1, m + n - 1

    while i >= 0 and j >= 0:
        if nums1[i] > nums2[j]:
            nums1[k] = nums1[i]
            i -= 1
        else:
            nums1[k] = nums2[j]
            j -= 1
        k -= 1

    while j >= 0:
        nums1[k] = nums2[j]
        j -= 1
        k -= 1

# 示例用法
nums1 = [1, 2, 3, 0, 0, 0]
m = 3
nums2 = [2, 5, 6]
n = 3
merge_sorted_arrays(nums1, m, nums2, n)
print(nums1)  # 输出:[1, 2, 2, 3, 5, 6]
More than half of the number problems appear in the array

To find elements that appear more than half the time in an array, you can use Moore's voting algorithm. This algorithm finds possible modes by maintaining candidate elements and counters.

def majority_element(nums):
    candidate = None
    count = 0

    for num in nums:
        if count == 0:
            candidate = num
        count += 1 if num == candidate else -1

    return candidate

# 示例用法
nums = [2, 2, 1, 1, 1, 2, 2]
majority = majority_element(nums)
print(majority)  # 输出:2
The maximum area of ​​the island problem

Finding the largest island area in a two-dimensional array can be achieved using the depth-first search (DFS) algorithm. Iterate through each element and when an island is encountered, do a DFS search and calculate the area of ​​the island.

def max_area_of_island(grid):
    def dfs(row, col):
        if row < 0 or row >= len(grid) or col < 0 or col >= len(grid[0]) or grid[row][col] == 0:
            return 0

        grid[row][col] = 0  # 将已访问的岛屿标记为0
        area = 1

        # 递归搜索四个方向
        area += dfs(row - 1, col)
        area += dfs(row + 1, col)
        area += dfs(row, col - 1)
        area += dfs(row, col + 1)

        return area

    max_area = 0

    for row in range(len(grid)):
        for col in range(len(grid[0])):
            if grid[row][col] == 1:
                max_area = max(max_area, dfs(row, col))

    return max_area

# 示例用法
grid = [
    [1, 1, 0, 0, 0],
    [1, 1, 0, 0, 0],
    [0, 0, 0, 1, 1],
    [0, 0, 0, 1, 1]
]
max_area = max_area_of_island(grid)
print(max_area)  # 输出:4
Rainwater catchment problem

The problem of collecting rainwater can be solved using the double-pointer method. Calculate the amount of rainwater that each location can receive by maintaining two pointers and two variables.

def trap(height):
    left, right = 0, len(height) - 1
    left_max, right_max = 0, 0
    water = 0

    while left < right:
        if height[left] < height[right]:
            if height[left] >= left_max:
                left_max = height[left]
            else:
                water += left_max - height[left]
            left += 1
        else:
            if height[right] >= right_max:
                right_max = height[right]
            else:
                water += right_max - height[right]
            right -= 1

    return water

# 示例用法
height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
rainwater = trap(height)
print(rainwater)  # 输出:6
spiral matrix problem

The spiral matrix problem is to traverse a two-dimensional matrix in spiral order. You can use simulated traversal to traverse the matrix layer by layer and add elements in spiral order.

def spiral_order(matrix):
    if not matrix:
        return []

    rows, cols = len(matrix), len(matrix[0])
    result = []

    left, right, top, bottom = 0, cols - 1, 0, rows - 1

    while left <= right and top <= bottom:
        # 从左到右
        for col in range(left, right + 1):
            result.append(matrix[top][col])
        # 从上到下
        for row in range(top + 1, bottom + 1):
            result.append(matrix[row][right])
        # 从右到左,确保不重复遍历同一行或同一列
        if left < right and top < bottom:
            for col in range(right - 1, left, -1):
                result.append(matrix[bottom][col])
            for row in range(bottom, top, -1):
                result.append(matrix[row][left])

        left += 1
        right -= 1
        top += 1
        bottom -= 1

    return result

# 示例用法
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]
spiral_order_result = spiral_order(matrix)
print(spiral_order_result)  # 输出:[1, 2, 3, 6, 9, 8, 7, 4, 5]
reverse order problem

The reverse pair problem is to count the number of reverse pairs in an array, that is, to find the number of pairs in the array that satisfy i < jand . It can be solved using the idea of ​​merge sort.nums[i] > nums[j](i, j)

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

        mid = left + (right - left) // 2
        count = merge_sort(nums, left, mid) + merge_sort(nums, mid + 1, right)
        
        # 统计逆序对数量
        i, j = left, mid + 1
        while i <= mid:
            while j <= right and nums[i] > 2 * nums[j]:
                j += 1
            count += j - (mid + 1)
            i += 1
        
        # 归并排序
        temp = []
        i, j = left, mid + 1
        while i <= mid and j <= right:
            if nums[i] <= nums[j]:
                temp.append(nums[i])
                i += 1
            else:
                temp.append(nums[j])
                j += 1
        temp.extend(nums[i:mid + 1])
        temp.extend(nums[j:right + 1])
        nums[left:right + 1] = temp

        return count

    return merge_sort(nums, 0, len(nums) - 1)

# 示例用法
nums = [7, 5, 6, 4]
reverse_pair_count = reverse_pairs(nums)
print(reverse_pair_count)  # 输出:5,包含的逆序对为 (7, 5), (7, 6), (7, 4), (5, 4), (6, 4)

4. Linked list

Reverse linked list

Linked list reversal is a common operation that can be implemented using iterative or recursive methods.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def reverse_linked_list(head):
    prev, current = None, head

    while current is not None:
        next_node = current.next
        current.next = prev
        prev = current
        current = next_node

    return prev

# 示例用法
# 创建一个链表:1 -> 2 -> 3 -> 4 -> 5
head = ListNode(1)
current = head
for i in range(2, 6):
    current.next = ListNode(i)
    current = current.next

reversed_head = reverse_linked_list(head)

# 输出反转后的链表:5 -> 4 -> 3 -> 2 -> 1
while reversed_head is not None:
    print(reversed_head.val, end=" -> ")
    reversed_head = reversed_head.next
A set of k reverse linked lists

Reversing a linked list into groups of k nodes is a common linked list operation.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def reverse_k_group(head, k):
    def reverse_linked_list(head):
        prev, current = None, head

        while current is not None:
            next_node = current.next
            current.next = prev
            prev = current
            current = next_node

        return prev

    def get_kth_node(head, k):
        for i in range(k):
            if head is None:
                return None
            head = head.next
        return head

    dummy = ListNode(0)
    dummy.next = head
    prev_group_tail = dummy

    while True:
        group_start = prev_group_tail.next
        group_end = get_kth_node(group_start, k)
        if group_end is None:
            break
        
        next_group_start = group_end.next
        group_end.next = None  # 切断当前组的链表
        prev_group_tail.next = reverse_linked_list(group_start)
        group_start.next = next_group_start
        prev_group_tail = group_start

    return dummy.next

# 示例用法
# 创建一个链表:1 -> 2 -> 3 -> 4 -> 5
head = ListNode(1)
current = head
for i in range(2, 6):
    current.next = ListNode(i)
    current = current.next

k = 3
reversed_head = reverse_k_group(head, k)

# 输出反转后的链表:3 -> 2 -> 1 -> 4 -> 5
while reversed_head is not None:
    print(reversed_head.val, end=" -> ")
    reversed_head = reversed_head.next
Remove duplicate elements from sorted linked list

Removing duplicate nodes in a sorted linked list is a simple linked list operation that can be implemented using an iterative method.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def delete_duplicates(head):
    current = head

    while current is not None and current.next is not None:
        if current.val == current.next.val:
            current.next = current.next.next
        else:
            current = current.next

    return head

# 示例用法
# 创建一个排序链表:1 -> 1 -> 2 -> 3 -> 3
head = ListNode(1)
head.next = ListNode(1)
head.next.next = ListNode(2)
head.next.next.next = ListNode(3)
head.next.next.next.next = ListNode(3)

new_head = delete_duplicates(head)

# 输出去重后的链表:1 -> 2 -> 3
while new_head is not None:
    print(new_head.val, end=" -> ")
    new_head = new_head.next
Circular linked list problem

Detecting whether there is a cycle in a linked list can be implemented using the fast and slow pointer method. The fast pointer moves two steps at a time, the slow pointer moves one step at a time, and if a loop exists, the two pointers will eventually meet.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def has_cycle(head):
    if not head or not head.next:
        return False

    slow = head
    fast = head.next

    while slow != fast:
        if not fast or not fast.next:
            return False
        slow = slow.next
        fast = fast.next.next

    return True

# 示例用法
# 创建一个带环的链表:1 -> 2 -> 3 -> 4 -> 5 -> 2 (重复节点2形成环)
head = ListNode(1)
current = head
for i in range(2, 6):
    current.next = ListNode(i)
    current = current.next
cycle_start = head.next

current.next = cycle_start  # 形成环

has_cycle_result = has_cycle(head)
print(has_cycle_result)  # 输出:True
The first common node problem of two linked lists

Finding the first common node of two linked lists can be achieved using the double pointer method. First, calculate the length difference between the two linked lists, then move the longer linked list by nodes with different lengths, and finally move the two linked lists at the same time to find the common node.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def get_intersection_node(headA, headB):
    def get_length(head):
        length = 0
        while head is not None:
            length += 1
            head = head.next
        return length

    lenA, lenB = get_length(headA), get_length(headB)

    # 让较长的链表先移动长度差个节点
    while lenA > lenB:
        headA = headA.next
        lenA -= 1
    while lenB > lenA:
        headB = headB.next
        lenB -= 1

    # 同时移动两个链表,找到第一个公共节点
    while headA != headB:
        headA = headA.next
        headB = headB.next

    return headA

# 示例用法
# 创建两个链表:
# 链表A:1 -> 2 -> 3
# 链表B:6 -> 7
# 公共节点:4 -> 5
headA = ListNode(1)
headA.next = ListNode(2)
headA.next.next = ListNode(3)

headB = ListNode(6)
headB.next = ListNode(7)

common_node = ListNode(4)
common_node.next = ListNode(5)

headA.next.next.next = common_node
headB.next.next = common_node

intersection_node = get_intersection_node(headA, headB)
print(intersection_node.val)  # 输出:4
Problem of merging ordered linked lists

Merging two ordered linked lists can be achieved using recursive or iterative methods.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def merge_sorted_lists(l1, l2):
    dummy = ListNode(0)
    current = dummy

    while l1 is not None and l2 is not None:
        if l1.val < l2.val:
            current.next = l1
            l1 = l1.next
        else:
            current.next = l2
            l2 = l2.next
        current = current.next

    if l1 is not None:
        current.next = l1
    if l2 is not None:
        current.next = l2

    return dummy.next

# 示例用法
# 创建两个有序链表:
# 链表1:1 -> 2 -> 4
# 链表2:1 -> 3 -> 4
head1 = ListNode(1)
head1.next = ListNode(2)
head1.next.next = ListNode(4)

head2 = ListNode(1)
head2.next = ListNode(3)
head2.next.next = ListNode(4)

merged_head = merge_sorted_lists(head1, head2)

# 输出合并后的有序链表:1 -> 1 -> 2 -> 3 -> 4 -> 4
while merged_head is not None:
    print(merged_head.val, end=" -> ")
    merged_head = merged_head.next
Linked list sum problem

Adding integers represented by two linked lists can be implemented using recursive or iterative methods, simulating the addition operation from low bit to high bit.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def add_two_numbers(l1, l2):
    dummy = ListNode(0)
    current = dummy
    carry = 0

    while l1 is not None or l2 is not None:
        x = l1.val if l1 is not None else 0
        y = l2.val if l2 is not None else 0

        total = x + y + carry
        carry = total // 10

        current.next = ListNode(total % 10)
        current = current.next

        if l1 is not None:
            l1 = l1.next
        if l2 is not None:
            l2 = l2.next

    if carry > 0:
        current.next = ListNode(carry)

    return dummy.next

# 示例用法
# 创建两个链表表示的整数:
# 链表1:2 -> 4 -> 3 (表示整数 342)
# 链表2:5 -> 6 -> 4 (表示整数 465)
head1 = ListNode(2)
head1.next = ListNode(4)
head1.next.next = ListNode(3)

head2 = ListNode(5)
head2.next = ListNode(6)
head2.next.next = ListNode(4)

result_head = add_two_numbers(head1, head2)

# 输出相加后的链表表示的整数:7 -> 0 -> 8 (表示整数 807)
while result_head is not None:
    print(result_head.val, end=" -> ")
    result_head = result_head.next
Palindrome linked list problem

Determining whether a linked list is a palindrome linked list can be achieved by using fast and slow pointers and linked list inversion. First use the fast and slow pointers to find the midpoint of the linked list, then reverse the second half of the linked list, and finally compare the two parts of the linked list to see if they are equal.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def is_palindrome(head):
    def reverse_linked_list(head):
        prev, current = None, head

        while current is not None:
            next_node = current.next
            current.next = prev
            prev = current
            current = next_node

        return prev

    slow, fast = head, head

    # 快慢指针找到中点
    while fast is not None and fast.next is not None:
        slow = slow.next
        fast = fast.next.next

    # 反转后半部分链表
    second_half = reverse_linked_list(slow)

    # 比较两部分链表是否相等
    while second_half is not None:
        if head.val != second_half.val:
            return False
        head = head.next
        second_half = second_half.next

    return True

# 示例用法
# 创建一个回文链表:1 -> 2 -> 2 -> 1
head = ListNode(1)
head.next = ListNode(2)
head.next.next = ListNode(2)
head.next.next.next = ListNode(1)

is_palindrome_result = is_palindrome(head)
print(is_palindrome_result)  # 输出:True
Copying a linked list with random pointers problem

Copying a linked list with random pointers can be done using a hash table, or it can be done by traversing the linked list multiple times.

class Node:
    def __init__(self, val=None, next=None, random=None):
        self.val = val
        self.next = next
        self.random = random

def copy_random_list(head):
    if not head:
        return None

    # 创建一个哈希表,用于存储原节点和对应的复制节点的映射关系
    mapping = {
    
    }

    current = head

    # 第一次遍历:复制链表节点,不处理random指针
    while current:
        mapping[current] = Node(current.val)
        current = current.next

    current = head

    # 第二次遍历:处理复制节点的random指针
    while current:
        if current.next:
            mapping[current].next = mapping[current.next]
        if current.random:
            mapping[current].random = mapping[current.random]
        current = current.next

    return mapping[head]

# 示例用法
# 创建一个带随机指针的链表:
# 1 -> 2 -> 3
# |    |
# v    v
# 3 -> 1
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)

node1.next = node2
node1.random = node3
node2.next = node3
node2.random = node1
node3.random = node1

copied_head = copy_random_list(node1)

# 输出复制后的带随机指针的链表:
# 1 -> 2 -> 3
# |    |
# v    v
# 3 -> 1
while copied_head:
    print(f"Value: {
      
      copied_head.val}, Random: {
      
      copied_head.random.val if copied_head.random else None}")
    copied_head = copied_head.next

These are specific examples of content in the linked list section, covering some common problems and solutions in linked list operations. These questions cover common problems such as linked list reversal, linked list grouping, deleting duplicate elements, detecting cycles, finding the first common node, merging ordered linked lists, summing linked lists, judging palindrome linked lists, and copying linked lists with random pointers.

5. Binary tree

depth of binary tree

Calculating the depth of a binary tree usually uses a recursive method, calculating the depth of the left subtree and the right subtree respectively, and then taking the larger value and adding 1.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def max_depth(root):
    if not root:
        return 0
    left_depth = max_depth(root.left)
    right_depth = max_depth(root.right)
    return max(left_depth, right_depth) + 1

# 示例用法
# 创建一个二叉树:
#     1
#    / \
#   2   3
#  / \
# 4   5
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

depth = max_depth(root)
print(depth)  # 输出:3
Zigzag printing binary tree problem

Zigzag printing binary tree can be implemented using a queue, by recording the nodes of the current layer, and then outputting the node values ​​in different hierarchical order.

from collections import deque

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def zigzag_level_order(root):
    if not root:
        return []

    result = []
    queue = deque([root])
    reverse = False

    while queue:
        level_values = []
        level_size = len(queue)

        for _ in range(level_size):
            node = queue.popleft()
            if reverse:
                level_values.insert(0, node.val)
            else:
                level_values.append(node.val)

            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

        result.append(level_values)
        reverse = not reverse

    return result

# 示例用法
# 创建一个二叉树:
#     3
#    / \
#   9  20
#      / \
#     15  7
root = TreeNode(3)
root.left = TreeNode(9)
root.right = TreeNode(20)
root.right.left = TreeNode(15)
root.right.right = TreeNode(7)

zigzag_result = zigzag_level_order(root)
# 输出之字形顺序的结果:[[3], [20, 9], [15, 7]]
print(zigzag_result)
The kth largest node problem of binary search tree

To find the k-th largest node in the binary search tree, a variant of in-order traversal can be used. Through reverse in-order traversal, first traverse the right subtree, root node, and left subtree, then you can traverse the nodes in descending order and find the k-th largest node.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def kth_largest(root, k):
    def reverse_inorder(node):
        nonlocal k
        if not node:
            return None

        result = reverse_inorder(node.right)
        if result is not None:
            return result

        k -= 1
        if k == 0:
            return node

        return reverse_inorder(node.left)

    return reverse_inorder(root).val

# 示例用法
# 创建一个二叉搜索树:
#     3
#    / \
#   1   4
#    \
#     2
root = TreeNode(3)
root.left = TreeNode(1)
root.left.right = TreeNode(2)
root.right = TreeNode(4)

k = 2
kth_largest_node = kth_largest(root, k)
print(kth_largest_node)  # 输出:2
Recent common ancestor problem for binary trees

Finding the nearest common ancestor of two nodes in a binary tree can be achieved using recursive methods. According to the nature of the most recent common ancestor, if a node is the most recent common ancestor of two nodes, then this node is either one of the nodes, or in the left subtree, or in the right subtree. By recursively traversing the left and right subtrees, find The most recent common ancestor of two nodes.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def lowest_common_ancestor(root, p, q):
    if not root:
        return None

    # 如果根节点是其中一个节点,则返回根节点
    if root == p or root == q:
        return root

    left_ancestor = lowest_common_ancestor(root.left, p, q)
    right_ancestor = lowest_common_ancestor(root.right, p, q)

    # 如果左子树和右子树都返回非空节点,说明分别找到了p和q,则当前节点是最近公共祖先
    if left_ancestor and right_ancestor:
        return root

    # 如果左子树返回非空节点,则返回左子树的结果
    return left_ancestor if left_ancestor else right_ancestor

# 示例用法
# 创建一个二叉树:
#     3
#    / \
#   5   1
#  / \ / \
# 6  2 0  8
#   / \
#  7   4
root = TreeNode(3)
root.left = TreeNode(5)
root.right = TreeNode(1)
root.left.left = TreeNode(6)
root.left.right = TreeNode(2)
root.left.right.left = TreeNode(7)
root.left.right.right = TreeNode(4)
root.right.left = TreeNode(0)
root.right.right = TreeNode(8)

p = root.left  # 节点5
q = root.right  # 节点1
ancestor = lowest_common_ancestor(root, p, q)
print(ancestor.val)  # 输出:3,最近公共祖先是根节点3
Path problem that sums to a certain value in a binary tree

Finding paths in a binary tree whose sum equals a given value can be achieved using a recursive approach. Starting from the root node, recursively traverse the left and right subtrees and accumulate the node values ​​​​on the path. When a leaf node is encountered and the path sum is equal to the given value, the path is added to the result.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def find_paths(root, target):
    def dfs(node, current_path, current_sum):
        if not node:
            return

        current_path.append(node.val)
        current_sum += node.val

        if not node.left and not node.right and current_sum == target:
            paths.append(list(current_path))
        
        dfs(node.left, current_path, current_sum)
        dfs(node.right, current_path, current_sum)

        # 回溯:恢复当前路径状态
        current_path.pop()

    paths = []
    dfs(root, [], 0)
    return paths

# 示例用法
# 创建一个二叉树:
#     5
#    / \
#   4   8
#  /   / \
# 11  13  4
# /  \    / \
# 7   2  5   1
root = TreeNode(5)
root.left = TreeNode(4)
root.right = TreeNode(8)
root.left.left = TreeNode(11)
root.right.left = TreeNode(13)
root.right.right = TreeNode(4)
root.left.left.left = TreeNode(7)
root.left.left.right = TreeNode(2)
root.right.right.left = TreeNode(5)
root.right.right.right = TreeNode(1)

target = 22
paths = find_paths(root, target)
# 输出路径和为22的所有路径:[[5, 4, 11, 2], [5, 8, 4, 5]]
print(paths)
Maximum path sum problem of binary tree

Finding the maximum path sum in a binary tree can be achieved using recursive methods. For each node, there are four situations to consider: a path that only contains the current node, a path that contains the current node and the left subtree, a path that contains the current node and the right subtree, a path that contains the current node, the left subtree, and the right subtree. path of. By recursively calculating the maximum of these four cases, the maximum path sum can be found.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def max_path_sum(root):
    def max_gain(node):
        nonlocal max_sum
        if not node:
            return 0

        # 递归计算左子树和右子树的最大贡献值
        left_gain = max(max_gain(node.left), 0)
        right_gain = max(max_gain(node.right), 0)

        # 计算以当前节点为根的最大路径和
        current_max_path = node.val + left_gain + right_gain

        # 更新全局最大路径和
        max_sum = max(max_sum, current_max_path)

        # 返回以当前节点为根的最大贡献值
        return node.val + max(left_gain, right_gain)

    max_sum = float('-inf')
    max_gain(root)
    return max_sum

# 示例用法
# 创建一个二叉树:
#    -10
#    / \
#   9  20
#      / \
#     15  7
root = TreeNode(-10)
root.left = TreeNode(9)
root.right = TreeNode(20)
root.right.left = TreeNode(15)
root.right.right = TreeNode(7)

max_path = max_path_sum(root)
print(max_path)  # 输出:42
Right view problem of binary tree

Obtaining the right view of the binary tree can be achieved using breadth-first search (BFS). At each level, the last node is taken from right to left and added to the result list.

from collections import deque

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def right_side_view(root):
    if not root:
        return []

    result = []
    queue = deque([root])

    while queue:
        level_size = len(queue)

        for i in range(level_size):
            node = queue.popleft()
            if i == level_size - 1:
                result.append(node.val)

            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

    return result

# 示例用法
# 创建一个二叉树:
#     1
#    / \
#   2   3
#    \   \
#     5   4
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.right = TreeNode(5)
root.right.right = TreeNode(4)

right_view = right_side_view(root)
# 输出右视图:[1, 3, 4]
print(right_view)

6. TopK

Minimum k number problem

Finding the smallest k numbers in an array can be achieved using a heap. Maintain a maximum heap of size k, traverse the array, and add elements to the heap in sequence. When the size of the heap exceeds k, pop up the top element of the heap. The remaining elements in the final heap are the smallest k numbers.

import heapq

def get_least_numbers(arr, k):
    if k <= 0 or k > len(arr):
        return []

    max_heap = []

    for num in arr:
        if len(max_heap) < k:
            heapq.heappush(max_heap, -num)
        else:
            if -num > max_heap[0]:
                heapq.heappop(max_heap)
                heapq.heappush(max_heap, -num)

    return [-num for num in max_heap]

# 示例用法
arr = [3, 2, 1, 5, 6, 4]
k = 2
least_numbers = get_least_numbers(arr, k)
print(least_numbers)  # 输出:[2, 1]
The Kth largest element problem in an array

Finding the k-th largest element in an array can also be implemented using a heap. Maintain a minimum heap of size k, traverse the array, and add elements to the heap in turn. When the size of the heap exceeds k, pop up the top element of the heap, and finally the heap The remaining elements in are the k-th largest element.

import heapq

def find_kth_largest(arr, k):
    if k <= 0 or k > len(arr):
        return None

    min_heap = arr[:k]
    heapq.heapify(min_heap)

    for num in arr[k:]:
        if num > min_heap[0]:
            heapq.heappop(min_heap)
            heapq.heappush(min_heap, num)

    return min_heap[0]

# 示例用法
arr = [3, 2, 1, 5, 6, 4]
k = 2
kth_largest = find_kth_largest(arr, k)
print(kth_largest)  # 输出:5

These are specific content examples on binary trees and TopK problems, covering binary tree depth calculation, zigzag printing binary tree, kth largest node of binary search tree, binary tree nearest common ancestor, binary tree path sum, binary tree maximum path sum, binary tree right view , as well as solutions to problems such as the smallest k numbers and the kth largest element in an array. Next, we will continue to explore solutions to design problems, dynamic programming, and other common problems.
The following is the specific content about design problems and dynamic programming problems:

7. Design questions

Minimum stack problem

Designing a minimal stack that supports constant time complexity can be achieved by simultaneously maintaining the minimum value in a normal stack. Each time it is pushed onto the stack, the current element is compared with the minimum value on the top of the stack, and the smaller value is pushed onto the stack. When popping from the stack, pop the top element and the minimum value of the stack at the same time.

class MinStack:
    def __init__(self):
        self.stack = []
        self.min_stack = []

    def push(self, val):
        self.stack.append(val)
        if not self.min_stack or val <= self.min_stack[-1]:
            self.min_stack.append(val)

    def pop(self):
        if self.stack:
            if self.stack[-1] == self.min_stack[-1]:
                self.min_stack.pop()
            self.stack.pop()

    def top(self):
        if self.stack:
            return self.stack[-1]

    def get_min(self):
        if self.min_stack:
            return self.min_stack[-1]

# 示例用法
min_stack = MinStack()
min_stack.push(2)
min_stack.push(0)
min_stack.push(3)
min_stack.push(0)

print(min_stack.get_min())  # 输出:0
min_stack.pop()
print(min_stack.get_min())  # 输出:0
min_stack.pop()
print(min_stack.get_min())  # 输出:0
min_stack.pop()
print(min_stack.get_min())  # 输出:2
Two stacks implement queue problem

Queues are implemented using two stacks, one for enqueuing operations and the other for dequeuing operations. When it is necessary to dequeue, the elements in the enqueuing stack are sequentially popped out of the stack and merged into the dequeuing stack, so that the dequeuing operation can be performed from the dequeuing stack.

class MyQueue:
    def __init__(self):
        self.in_stack = []
        self.out_stack = []

    def push(self, val):
        self.in_stack.append(val)

    def pop(self):
        if not self.out_stack:
            while self.in_stack:
                self.out_stack.append(self.in_stack.pop())
        if self.out_stack:
            return self.out_stack.pop()

    def peek(self):
        if not self.out_stack:
            while self.in_stack:
                self.out_stack.append(self.in_stack.pop())
        if self.out_stack:
            return self.out_stack[-1]

    def empty(self):
        return not self.in_stack and not self.out_stack

# 示例用法
queue = MyQueue()
queue.push(1)
queue.push(2)
print(queue.peek())  # 输出:1
print(queue.pop())   # 输出:1
print(queue.empty()) # 输出:False
LRU caching mechanism problem

The design and implementation of LRU (Least Recently Used) caching mechanism can be implemented using hash tables and doubly linked lists. Hash tables are used to quickly find elements in the cache, and doubly linked lists are used to maintain the access order of elements.

class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = {
    
    }
        self.head = Node()
        self.tail = Node()
        self.head.next = self.tail
        self.tail.prev = self.head

    def _add_node(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node

    def _remove_node(self, node):
        prev = node.prev
        next_node = node.next
        prev.next = next_node
        next_node.prev = prev

    def _move_to_head(self, node):
        self._remove_node(node)
        self._add_node(node)

    def _pop_tail(self):
        res = self.tail.prev
        self._remove_node(res)
        return res

    def get(self, key):
        if key in self.cache:
            node = self.cache[key]
            self._move_to_head(node)
            return node.value
        return -1

    def put(self, key, value):
        if key in self.cache:
            node = self.cache[key]
            node.value = value
            self._move_to_head(node)
        else:
            if len(self.cache) >= self.capacity:
                tail = self._pop_tail()
                del self.cache[tail.key]
            new_node = Node(key, value)
            self.cache[key] = new_node
            self._add_node(new_node)

class Node:
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

# 示例用法
cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
print(cache.get(1)) # 输出:1
cache.put(3, 3)
print(cache.get(2)) # 输出:-1,因为键2已被移除

8. Dynamic programming

frog jumping problem

The frog jumping problem can be regarded as a typical dynamic programming problem. Each time you can jump 1 or 2 steps, then the number of ways to jump to the nth step is equal to the number of ways to jump to the n-1th step plus The number of ways to jump to the n-2th step, that is, the Fibonacci sequence.

def climb_stairs(n):
    if n <= 2:
        return n
    dp = [0] * (n + 1)
    dp[1] = 1
    dp[2] = 2
    for i in range(3, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n]

# 示例用法
n = 4
ways = climb_stairs(n)
print(ways)  # 输出:5
Longest rising subsequence problem

The longest rising subsequence problem is a classic dynamic programming problem that can be solved using dynamic programming. Define a state array dp, where dp[i] represents the length of the longest ascending subsequence ending with the i-th element. Initially, each element forms an ascending subsequence of length 1, and then traverses the array. For each element, find all elements before it that are smaller than it, and calculate the length of the longest ascending subsequence ending with this element.

def length_of_lis(nums):
    if not nums:
        return 0
    n = len(nums)
    dp = [1] * n

    for i in range(1, n):
        for j in range(i):
            if nums[i] > nums[j]:
                dp[i] = max(dp[i], dp[j] + 1)

    return max(dp)

# 示例用法
nums = [10, 9, 2, 5, 3, 7, 101, 18]
length = length_of_lis(nums)
print(length)  # 输出:5,最长上升子序列是[2, 3, 7, 101]
longest common subsequence problem

The longest common subsequence problem can be solved using dynamic programming. Define a two-dimensional array dp, where dp[i][j] represents the length of the longest common subsequence of the first i characters of string text1 and the first j characters of string text2. According to the state transition equation of dynamic programming, the entire dp array can be filled.

def longest_common_subsequence(text1, text2):
    m, n = len(text1), len(text2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]

    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if text1[i - 1] == text2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

    return dp[m][n]

# 示例用法
text1 = "abcde"
text2 = "ace"
length = longest_common_subsequence(text1, text2)
print(length)  # 输出:3,最长公共子序列是"ace"
Edit distance problem

The edit distance problem is a classic string matching problem that can be solved using dynamic programming. Define a two-dimensional array dp, where dp[i][j] represents the minimum edit distance required to convert the first i characters of the string word1 into the first j characters of the string word2. By filling the dp array, the minimum edit distance can be obtained.

def min_distance(word1, word2):
    m, n = len(word1), len(word2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]

    for i in range(m + 1):
        for j in range(n + 1):
            if i == 0:
                dp[i][j] = j
            elif j == 0:
                dp[i][j] = i
            elif word1[i - 1] == word2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1]
            else:
                dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])

    return dp[m][n]

# 示例用法
word1 = "horse"
word2 = "ros"
distance = min_distance(word1, word2)
print(distance)  # 输出:3,最小编辑距离为3,删除'h',替换'r',添加's'
Change exchange 2 problem

The change exchange 2 problem is a dynamic programming problem. Dynamic programming can be used to calculate the number of ways that coins of different denominations can be combined into a specified amount. Define a one-dimensional array dp, where dp[i] represents the number of ways to combine the amount i. Initially, dp[0] is 1, which means that the number of ways to combine the amount into 0 is 1. The remaining elements are initialized to 0, and then the coin denominations are traversed and the dp array is updated.

def change(amount, coins):
    dp = [0] * (amount + 1)
    dp[0] = 1

    for coin in coins:
        for i in range(coin, amount + 1):
            dp[i] += dp[i - coin]

    return dp[amount]


# 示例用法
amount = 5
coins = [1, 2, 5]
combinations = change(amount, coins)
print(combinations)  # 输出:4,有4种组合方式:[1, 1, 1, 1, 1], [1, 1, 1, 2], [2, 2, 1], [5]

These are specific examples of design problems and dynamic programming problems, covering minimum stack, two stack implementation queue, LRU cache mechanism, frog jumping steps, longest rising subsequence, longest common subsequence, edit distance, change exchange 2 solutions to the problem. Next, we'll move on to solutions to some other common problems.

9. Others

flip word order problem

The flipped word order problem can be solved by first flipping the entire string and then flipping the character order of each word.

def reverse_words(s):
    s = s[::-1]  # 翻转整个字符串
    words = s.split()  # 切分单词
    return ' '.join(words[::-1])  # 翻转单词顺序并拼接成字符串

# 示例用法
s = "the sky is blue"
reversed_s = reverse_words(s)
print(reversed_s)  # 输出:"blue is sky the"
Question about the number of 1’s in binary system

Counting the number of 1's in the binary representation of an integer can be accomplished using bitwise arithmetic by continuously shifting the integer one bit to the right and performing an AND operation with 1's until the integer is 0.

def hamming_weight(n):
    count = 0
    while n:
        count += n & 1
        n >>= 1
    return count

# 示例用法
n = 11
count = hamming_weight(n)
print(count)  # 输出:3,二进制表示为1011,含有3个1
Reversing binary bits problem

Reversing the binary bits of an unsigned integer can be achieved by continuously taking out and shifting the lowest bit of the integer to the left, taking the highest bit of the result and shifting it to the left, and then ORing them.

def reverse_bits(n):
    result = 0
    for _ in range(32):
        result <<= 1
        result |= n & 1
        n >>= 1
    return result

# 示例用法
n = 43261596
reversed_n = reverse_bits(n)
print(reversed_n)  # 输出:964176192
Median problem in data streams

Design a data structure that supports obtaining the median at any time in the data stream, which can be implemented using two heaps. A large top heap is used to store the smaller half of the data, and a small top heap is used to store the larger half of the data.

import heapq

class MedianFinder:
    def __init__(self):
        self.small_heap = []  # 小顶堆,存储较大的一半数据
        self.large_heap = []  # 大顶堆,存储较小的一半数据

    def add_num(self, num):
        if not self.small_heap or num > -self.small_heap[0]:
            heapq.heappush(self.small_heap, -num)
        else:
            heapq.heappush(self.large_heap, num)

        # 平衡两个堆的大小
        if len(self.small_heap) > len(self.large_heap) + 1:
            heapq.heappush(self.large_heap, -heapq.heappop(self.small_heap))
        elif len(self.large_heap) > len(self.small_heap):
            heapq.heappush(self.small_heap, -heapq.heappop(self.large_heap))

    def find_median(self):
        if len(self.small_heap) == len(self.large_heap):
            return (-self.small_heap[0] + self.large_heap[0]) / 2.0
        else:
            return -self.small_heap[0]


# 示例用法
median_finder = MedianFinder()
median_finder.add_num(1)
median_finder.add_num(2)
median_finder.add_num(3)
median = median_finder.find_median()
print(median)  # 输出:2.0,因为中位数是2

median_finder.add_num(4)
median = median_finder.find_median()
print(median)  # 输出:2.5,因为中位数是 (2 + 3) / 2 = 2.5

Restoring IP address issues

The problem of recovering an IP address is a backtracking algorithm problem, which can enumerate all possible IP address combinations through the backtracking algorithm.

def restore_ip_addresses(s):
    def backtrack(start, path):
        if len(path) == 4:
            if start == len(s):
                result.append('.'.join(path))
            return
        for i in range(1, 4):
            if start + i <= len(s):
                segment = s[start:start + i]
                if (len(segment) == 1 or (len(segment) > 1 and segment[0] != '0')) and 0 <= int(segment) <= 255:
                    path.append(segment)
                    backtrack(start + i, path)
                    path.pop()

    result = []
    backtrack(0, [])
    return result

# 示例用法
s = "25525511135"
ip_addresses = restore_ip_addresses(s)
print(ip_addresses)  # 输出:["255.255.11.135", "255.255.111.35"]

These are solutions to other common problems, including reversing the order of words, the number of 1's in a binary, reversing binary bits, medians in data streams, and recovering IP addresses. Next, we'll continue our series on some frequently asked questions.

Of course, here are examples of specific content for some of the questions in the series:

10. Series of questions

Sum of X numbers series
sum of two numbers problem

The sum of two numbers problem is a common problem that can be solved using a hash table. Traverse the array, and for each element, check whether the difference between the target value and the current element is in the hash table. If it is, the sum of the two numbers is found to be equal to the target value.

def two_sum(nums, target):
    num_to_index = {
    
    }
    for i, num in enumerate(nums):
        complement = target - num
        if complement in num_to_index:
            return [num_to_index[complement], i]
        num_to_index[num] = i
    return None

# 示例用法
nums = [2, 7, 11, 15]
target = 9
result = two_sum(nums, target)
print(result)  # 输出:[0, 1],因为 nums[0] + nums[1] = 2 + 7 = 9
Sum of three numbers problem

The sum of three numbers problem can be transformed into the sum of two numbers problem. First sort the array, then fix one number, and then use the double pointer method to find the other two numbers so that their sum is equal to the target value.

def three_sum(nums):
    nums.sort()
    result = []
    n = len(nums)

    for i in range(n - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0:
                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
            elif total < 0:
                left += 1
            else:
                right -= 1

    return result

# 示例用法
nums = [-1, 0, 1, 2, -1, -4]
result = three_sum(nums)
print(result)  # 输出:[[-1, -1, 2], [-1, 0, 1]]
The nearest sum of three numbers problem

The closest sum of three numbers problem is a variant. You can find the sum closest to the target value on the premise that the sum of three numbers is equal to the target value.

def three_sum_closest(nums, target):
    nums.sort()
    closest_sum = float('inf')
    min_diff = float('inf')
    n = len(nums)

    for i in range(n - 2):
        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            diff = abs(total - target)
            if diff < min_diff:
                min_diff = diff
                closest_sum = total
            if total < target:
                left += 1
            elif total > target:
                right -= 1
            else:
                return closest_sum

    return closest_sum

# 示例用法
nums = [-1, 2, 1, -4]
target = 1
result = three_sum_closest(nums, target)
print(result)  # 输出:2,最接近目标值1的和为2

These are solutions to a series of problems about the sum of X numbers, including solutions to problems such as the sum of two numbers, the sum of three numbers, the sum of the nearest three numbers, etc. These questions cover finding the sum of numbers in an array equal to a target value, as well as finding the combination that satisfies a requirement under certain conditions.

Stock Series
Best time to buy and sell stocks 1 question

The best time to buy and sell stocks1 problem can be solved by maintaining the lowest stock price and maximum profit. Traverse the stock price array, and for each stock price, update the minimum stock price and maximum profit.

def max_profit(prices):
    min_price = float('inf')
    max_profit = 0

    for price in prices:
        min_price = min(min_price, price)
        max_profit = max(max_profit, price - min_price)

    return max_profit

# 示例用法
prices = [7, 1, 5, 3, 6, 4]
profit = max_profit(prices)
print(profit)  # 输出:5,最大利润为5,买入价格为1,卖出价格为6
Best time to buy and sell stocks 2 questions

The best time to buy and sell stocks 2 problem is a greedy algorithm problem. It can be traversed through the stock price array. For each day, if the stock price is higher than the previous day, the profit of this day is added to the total profit.

def max_profit2(prices):
    max_profit = 0

    for i in range(1, len(prices)):
        if prices[i] > prices[i - 1]:
            max_profit += prices[i] - prices[i - 1]

    return max_profit

# 示例用法
prices = [7, 1, 5, 3, 6, 4]
profit = max_profit2(prices)
print(profit)  # 输出:7,最大利润为7,买入价格为1,卖出价格为5,再买入价格为3,卖出价格为6
The best time to buy and sell stocks 3 questions

The best time to buy and sell stocks 3 problem can be solved by dynamic programming. Define a two-dimensional array dp, where dp[i][j][k] indicates that at the end of the i-th day, at most j transactions are made and holding (k=1) or not holding (k=0) stocks maximum profit. Fill the dp array according to the state transition equation.

def max_profit3(prices):
    if not prices:
        return 0

    max_transactions = 2  # 最多交易次数
    n = len(prices)
    dp = [[[0] * 2 for _ in range(max_transactions + 1)] for _ in range(n)]

    for i in range(n):
        for j in range(max_transactions, 0, -1):
            if i == 0:
                dp[i][j][1] = -prices[i]
            else:
                dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i])
                dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i])

    return dp[n - 1][max_transactions][0]

# 示例用法
prices = [3, 3, 5, 0, 0, 3, 1, 4]
profit = max_profit3(prices)
print(profit)  # 输出:6,最大利润为6,买入价格为3,卖出价格为5,再买入价格为0,卖出价格为3

These are solutions to a series of questions about stocks, including the best time to buy and sell stocks 1, the best time to buy and sell stocks 2, and the best time to buy and sell stocks 3. These questions cover different situations and constraints in stock trading, helping you understand how to make informed decisions in the stock market.

bracket series
Valid parentheses issue

The valid parentheses problem is a classic application of the stack. Use the stack to store the left parenthesis. When the right parenthesis is encountered, check whether the top of the stack matches it. If it matches, pop it from the stack. Otherwise, it does not match.

def is_valid(s):
    stack = []
    mapping = {
    
    ')': '(', '}': '{', ']': '['}
    
    for char in s:
        if char in mapping:
            top_element = stack.pop() if stack else '#'
            if mapping[char] != top_element:
                return False
        else:
            stack.append(char)
    
    return not stack

# 示例用法
s = "()[]{}"
result = is_valid(s)
print(result)  # 输出:True
Longest valid bracket problem

The longest valid bracket problem can be solved using a stack. Traverse the string and use the stack to store the index of the unmatched left bracket. When encountering the right bracket, check whether the stack is empty. If not, calculate the distance between the current position and the top element of the stack. This distance is the current valid bracket. length.

def longest_valid_parentheses(s):
    stack = [-1]  # 初始化栈,用-1表示栈底
    max_length = 0
    
    for i in range(len(s)):
        if s[i] == '(':
            stack.append(i)
        else:
            stack.pop()
            if not stack:
                stack.append(i)
            else:
                max_length = max(max_length, i - stack[-1])
    
    return max_length

# 示例用法
s = "(()())"
result = longest_valid_parentheses(s)
print(result)  # 输出:6,最长有效括号为"(()())"

The above is the solution to the bracket series problem, including determining valid brackets and finding the longest valid bracket substring. These problems involve common scenarios of bracket matching and are also classic problems in algorithms and data structures.

Summarize

This article provides an in-depth introduction to a series of common algorithm and data structure problems and their related questions on LeetCode. These questions cover key concepts in computer science and programming, including basic data structures, commonly used algorithmic ideas, and dynamic programming. By learning and practicing these questions, you'll be able to sharpen your programming skills, better prepare yourself for interview challenges, and be able to solve real-world problems more efficiently. I hope this article will be helpful to your learning and development in the field of algorithms and data structures.

Guess you like

Origin blog.csdn.net/qq_42531954/article/details/133280951