title: Essay on interview questions: data structure description: In Python interviews, language features are a must. Understanding these underlying mechanisms will not only help you cope with interviews, but also avoid writing bugs that are difficult to troubleshoot in actual development.

Data structures and algorithms are eternal themes in technical interviews. This article will use the most refined code and the most popular explanations to connect the most frequent data structure test points in interviews, helping you weave scattered knowledge points into a usable network.

1. Red-black tree: Why is the balanced tree "intentionally" not strictly balanced?

Interviewers like to ask: Linux kernel, JavaHashMapWhy do some of Python's dictionary implementations choose red-black trees instead of the more "perfect" AVL trees?

Summary in one sentence: The red-black tree uses acceptable search performance in exchange for faster writing speed. It is a "just right" balanced tree in engineering.

  • AVL tree: Absolutely balanced, the height difference between the left and right subtrees of any node does not exceed 1.

  • Advantages: The search is extremely fast, suitable for scenarios where there is a lot of reading and little writing (such as a read-only database index).

  • Disadvantages: Frequent rotation is required to restore strict balance during insertion and deletion, and writing performance is poor.

  • Red-Black Tree: Only a "black perfect balance" is guaranteed, the height difference can be relaxed to about 2 times.

  • Advantages: Insertion and deletion can be repaired with up to 3 rotations, and the writing performance is much better than AVL.

  • Disadvantages: The search is slightly slower, but this difference in memory is almost negligible.

Interview Highlights If your system searches far more than it modifies → consider AVL. If it is a general container that is searched and modified frequently → red-black tree is the best compromise.


2. Classic recursion and dynamic programming: start by jumping steps

Recursion is the starting point of dynamic programming. If you can see overlapping sub-problems from the recursion tree, you have already started with DP.

2.1 Basic step problem

A frog can jump 1 or 2 steps at a time. Find the number of ways to jump up n steps. This is a variation of the Fibonacci sequence.

The best way to write it: space optimization iteration

def fib(n):
    # 迭代法:时间 O(n),空间 O(1)
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return b

No need to recurse, no need to store the entire table, just rely on two variables to solve the problem. This is the way to write for an interview, neatly and neatly.

2.2 Abnormal steps problem

What if you can jump 1, 2, ..., n levels at a time? To sum up the mathematics, the method number is 2 raised to the n-1 power.

f = lambda n: 1 << (n - 1)  # 位运算比 pow、乘方快得多

This little trick shows how sensitive you are to performance.


3. Young’s matrix search: cut rows and columns with a pair of scissors

Young's matrix: Each row increases from left to right, and each column increases from top to bottom. How to find the target value in such a matrix to achieve O(m+n)?

Idea: Standing in the upper right corner, this position is the dividing line between "heaven" and "hell":

  • If the current value is greater than the target, it means that there is no target in the entire column → move one column to the left;
  • If the current value is smaller than the target, it means that the entire row cannot have a target → move down one row.
def find_in_matrix(matrix, target):
    if not matrix:
        return False
    row, col = 0, len(matrix[0]) - 1
    while row < len(matrix) and col >= 0:
        val = matrix[row][col]
        if val == target:
            return True
        elif val > target:
            col -= 1
        else:
            row += 1
    return False

What the interviewer appreciates most is this intuition of "cutting off an entire row or column at a time" - no need to divide it into two parts, but it is more beautiful than two parts.


4. Linked list high-frequency skills: the art of pointer dancing

The linked list question tests your control over disconnection and reconnection. Never write in a circle on the paper.

4.1 Exchange linked list nodes in pairs

Bundle1 -> 2 -> 3 -> 4become2 -> 1 -> 4 -> 3. Recursive writing is naturally suitable for this nested structure:

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

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        # 终止条件:空节点或只剩一个节点
        if head is None or head.next is None:
            return head
        
        next_node = head.next
        # 递归处理后续节点
        head.next = self.swapPairs(next_node.next)
        # 完成当前对调
        next_node.next = head
        
        return next_node

The recursive framework is as clear as poetry: first believe that the following ones have been arranged, and then deal with the pair in front of you.

4.2 Finding intersection points in cross-linked lists: romantic encounter method

Two linked lists may intersect at a node. How to find the intersection point in space O(1)?

Elegant solution: Two pointers A and B traverse at the same time. After finishing their own linked list, they immediately jump to the head of the other party's linked list and continue walking. Since the total length traveled is the same, they must meet at the intersection; if there is no intersection, they will walk at the same timeNone

def get_intersection_node(headA, headB):
    pA, pB = headA, headB
    while pA != pB:
        pA = pA.next if pA else headB
        pB = pB.next if pB else headA
    return pA

4.3 Singly linked list inversion: the most classic pointer operation

To reverse a one-way linked list, every pointer must "turn back". The core is just four steps, and you can deduce them backhand if you memorize them:

def reverse_linked_list(head):
    if not head or not head.next:
        return head
        
    pre, cur = None, head
    while cur:
        tmp = cur.next   # 1. 暂存下一个节点,防止断链
        cur.next = pre   # 2. 反转当前指针
        pre = cur        # 3. pre 前移
        cur = tmp        # 4. cur 前移
    return pre

5. Python practical container operations

This type of question does not test algorithms, but your familiarity with language features. A beautiful line of code is worth a long paragraph.

5.1 List deduplication and maintain order

Python 3.7+ dictionaries are naturally ordered and can be used wiselydict.fromkeys

items = ['b','c','d','b','c','a','a']
unique_items = list(dict.fromkeys(items))
# 结果:['b', 'c', 'd', 'a']

This is better thansorted(set(...), key=items.index)I don’t know how high it got.

5.2 Various ways to create a dictionary

  1. Literal (most recommended):
    d = {'name': 'earth', 'port': '80'}
  2. Factory function dict:
    d = dict([('name', 'earth'), ('port', '80')])
    d = dict(name='earth', port='80')          # 关键字参数
  3. fromkeys method (batch initialization):
    d = {}.fromkeys(('x', 'y'), -1)

⚠️ Note: If the default value is a mutable object (such as a list), all keys will share the same object, and modifying one will pollute them all!

5.3 Merge two ordered lists

This is the core step of merge sort. The key point ispop(0)Very slow, be sure to use double pointers:

def merge_sorted_list(a, b):
    result = []
    i = j = 0
    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            result.append(a[i])
            i += 1
        else:
            result.append(b[j])
            j += 1
    result.extend(a[i:])
    result.extend(b[j:])
    return result

6.1 Binary search: The boundary is the midpoint, success or failure lies in the details

It is not easy to write a binary search correctly. There are two pitfalls to pay attention to:

  • midDon’t calculate directly(low+high)//2, may overflow (Python will not, but the style should be language-neutral).
  • The loop condition iswhile low <= high,deal withtargetIn borderline situations.
def binary_search(arr, target):
    low, high = 0, len(arr) - 1
    
    while low <= high:
        mid = low + (high - low) // 2
        guess = arr[mid]
        if guess == target:
            return mid
        elif guess > target:
            high = mid - 1
        else:
            low = mid + 1
    return None

6.2 Quick sort: The pearl of divide and conquer thinking

Quick sort embodies the perfect logic of "divide and conquer": select pivot, divide into two sides, and sort them recursively.

def quicksort(arr):
    if len(arr) < 2:
        return arr
    
    pivot = arr[0]   # 可以优化为随机选或三数取中
    less = [i for i in arr[1:] if i <= pivot]
    greater = [i for i in arr[1:] if i > pivot]
    
    return quicksort(less) + [pivot] + quicksort(greater)

7. Binary tree high-frequency test points

7.1 Node definition and level traversal

class Node:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

# 示例树
tree = Node(1,
            Node(3, Node(7, Node(0)), Node(6)),
            Node(2, Node(5), Node(4)))

Level traversal (BFS) is the basis of range breadth first:

def level_order_lookup(root):
    if not root:
        return []
    result = []
    current_row = [root]
    while current_row:
        result.append([node.data for node in current_row])
        next_row = []
        for node in current_row:
            if node.left:
                next_row.append(node.left)
            if node.right:
                next_row.append(node.right)
        current_row = next_row
    return result

7.2 Depth-first traversal: three steps to determine the outcome

By changing the timing of accessing the root node, you can have fun with just three lines of code:

# 前序:根 -> 左 -> 右
def pre_order(root, res=None):
    if res is None: res = []
    if not root: return res
    res.append(root.data)
    pre_order(root.left, res)
    pre_order(root.right, res)
    return res

# 中序:左 -> 根 -> 右
def mid_order(root, res=None):
    if res is None: res = []
    if not root: return res
    mid_order(root.left, res)
    res.append(root.data)
    mid_order(root.right, res)
    return res

# 后序:左 -> 右 -> 根
def post_order(root, res=None):
    if res is None: res = []
    if not root: return res
    post_order(root.left, res)
    post_order(root.right, res)
    res.append(root.data)
    return res

7.3 Two basic formulas

# 最大树深
def get_max_depth(root):
    if not root:
        return 0
    return max(get_max_depth(root.left), get_max_depth(root.right)) + 1

# 判断两棵树是否相同
def is_same_tree(p, q):
    if not p and not q:
        return True
    if not p or not q or p.data != q.data:
        return False
    return is_same_tree(p.left, q.left) and is_same_tree(p.right, q.right)

8. Other classic algorithms

8.1 Anagram Judgment: Optimal Solution for Lowercase Letter Scenario

class Anagram:
    @staticmethod
    def is_anagram_sort(s1, s2):
        # 排序法:简洁,时间 O(n log n)
        return sorted(s1) == sorted(s2)

    @staticmethod
    def is_anagram_count(s1, s2):
        # 计数法:性能最高,时间 O(n),空间 O(1)
        if len(s1) != len(s2):
            return False
        counts = [0] * 26
        for ch in s1:
            counts[ord(ch) - 97] += 1
        for ch in s2:
            counts[ord(ch) - 97] -= 1
        return all(c == 0 for c in counts)

During the interview, the sorting method is used first to show the ideas, and then the counting method is followed to show the optimization awareness, which has the best effect.

8.2 The problem of finding change: first experience with dynamic programming

Given an array of denominations and an amount, find the minimum number of coins. This is DP’s hello world.

def coin_change(values, money):
    dp = [float('inf')] * (money + 1)
    dp[0] = 0
    
    for cents in range(1, money + 1):
        for coin in values:
            if coin <= cents:
                dp[cents] = min(dp[cents], dp[cents - coin] + 1)
                
    return dp[money] if dp[money] != float('inf') else -1

Tips: Initializationfloat('inf')It is safer than large numbers. Finally, check whether it is still infinite to determine that there is no solution.


The above content ranges from data structures to practical algorithms, and each paragraph condenses common test routines at the interview site. By mastering these core models proficiently, you will not only be able to write beautiful code, but also be able to calmly respond to the interviewer's questions. I wish you a wonderful appearance!