数据结构基础

什么是数据结构?

数据结构是计算机科学中组织和存储数据的方式,用于高效地访问和修改数据。

常见的数据结构:

  • 线性结构:数组、链表、栈、队列
  • 树形结构:二叉树、堆、B树
  • 图结构:有向图、无向图
  • 哈希结构:哈希表、哈希映射

栈(Stack)

什么是栈?

栈是一种后进先出(LIFO, Last In First Out)的线性数据结构,只能在栈顶进行插入和删除操作。

栈的特点:

  • 后进先出:最后入栈的元素最先出栈
  • 只能在栈顶操作:只能在栈顶进行插入(push)和删除(pop)
  • 有限大小:栈的大小通常是固定的

栈的操作:

  • **push(x)**:将元素 x 压入栈顶
  • **pop()**:删除并返回栈顶元素
  • **top()/peek()**:返回栈顶元素(不删除)
  • **empty()**:判断栈是否为空
  • **size()**:返回栈中元素的个数

栈的实现

使用数组实现栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ArrayStack:
def __init__(self, capacity=100):
self._stack = []
self._capacity = capacity

def push(self, item):
if len(self._stack) >= self._capacity:
raise OverflowError("Stack is full")
self._stack.append(item)

def pop(self):
if self.is_empty():
raise IndexError("Stack is empty")
return self._stack.pop()

def top(self):
if self.is_empty():
raise IndexError("Stack is empty")
return self._stack[-1]

def is_empty(self):
return len(self._stack) == 0

def size(self):
return len(self._stack)

使用链表实现栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next

class LinkedStack:
def __init__(self):
self._head = None
self._size = 0

def push(self, item):
new_node = ListNode(item)
new_node.next = self._head
self._head = new_node
self._size += 1

def pop(self):
if self.is_empty():
raise IndexError("Stack is empty")
val = self._head.val
self._head = self._head.next
self._size -= 1
return val

def top(self):
if self.is_empty():
raise IndexError("Stack is empty")
return self._head.val

def is_empty(self):
return self._head is None

def size(self):
return self._size

Python 内置的栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Python 中可以使用 list 作为栈
stack = []

# push
stack.append(1)
stack.append(2)
stack.append(3)

# pop
top = stack.pop() # 返回 3

# top
top = stack[-1] # 返回 2,不删除

# empty
is_empty = len(stack) == 0

栈的应用

1. 表达式求值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def evaluate_expression(expression):
"""使用栈计算中缀表达式"""
def precedence(op):
if op in '+-':
return 1
if op in '*/':
return 2
return 0

def apply_operator(values, operators):
right = values.pop()
left = values.pop()
op = operators.pop()
if op == '+':
values.append(left + right)
elif op == '-':
values.append(left - right)
elif op == '*':
values.append(left * right)
elif op == '/':
values.append(left / right)

values = []
operators = []
i = 0

while i < len(expression):
if expression[i] == ' ':
i += 1
continue

if expression[i].isdigit():
num = 0
while i < len(expression) and expression[i].isdigit():
num = num * 10 + int(expression[i])
i += 1
values.append(num)
continue

if expression[i] == '(':
operators.append(expression[i])
elif expression[i] == ')':
while operators and operators[-1] != '(':
apply_operator(values, operators)
operators.pop()
else:
while operators and precedence(operators[-1]) >= precedence(expression[i]):
apply_operator(values, operators)
operators.append(expression[i])

i += 1

while operators:
apply_operator(values, operators)

return values[0] if values else 0

2. 括号匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
def is_valid_parentheses(s):
"""判断括号是否匹配"""
stack = []
mapping = {')': '(', ']': '[', '}': '{'}

for char in s:
if char in mapping:
if not stack or stack.pop() != mapping[char]:
return False
else:
stack.append(char)

return len(stack) == 0

3. 中缀转后缀表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def infix_to_postfix(expression):
"""将中缀表达式转换为后缀表达式"""
def precedence(op):
if op in '+-':
return 1
if op in '*/':
return 2
return 0

result = []
stack = []

for char in expression:
if char.isdigit() or char.isalpha():
result.append(char)
elif char == '(':
stack.append(char)
elif char == ')':
while stack and stack[-1] != '(':
result.append(stack.pop())
stack.pop()
else:
while stack and stack[-1] != '(' and precedence(stack[-1]) >= precedence(char):
result.append(stack.pop())
stack.append(char)

while stack:
result.append(stack.pop())

return ''.join(result)

4. 单调栈

单调栈是一种特殊的栈,栈中的元素保持单调性(递增或递减)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def next_greater_element(nums):
"""找出每个元素右边第一个比它大的元素"""
result = [-1] * len(nums)
stack = [] # 存储索引,保持单调递减

for i in range(len(nums)):
while stack and nums[stack[-1]] < nums[i]:
index = stack.pop()
result[index] = nums[i]
stack.append(i)

return result

def next_smaller_element(nums):
"""找出每个元素右边第一个比它小的元素"""
result = [-1] * len(nums)
stack = [] # 存储索引,保持单调递增

for i in range(len(nums)):
while stack and nums[stack[-1]] > nums[i]:
index = stack.pop()
result[index] = nums[i]
stack.append(i)

return result

最小栈(Min Stack)

最小栈是一种特殊的栈,可以在 O(1) 时间复杂度内返回栈中的最小元素。

实现方式 1:辅助栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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)
else:
self._min_stack.append(self._min_stack[-1])

def pop(self):
if not self._stack:
raise IndexError("Stack is empty")
self._stack.pop()
self._min_stack.pop()

def top(self):
if not self._stack:
raise IndexError("Stack is empty")
return self._stack[-1]

def get_min(self):
if not self._min_stack:
raise IndexError("Stack is empty")
return self._min_stack[-1]

实现方式 2:存储差值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class MinStackOptimized:
def __init__(self):
self._stack = []
self._min = None

def push(self, val):
if not self._stack:
self._stack.append(0)
self._min = val
else:
diff = val - self._min
self._stack.append(diff)
if diff < 0:
self._min = val

def pop(self):
if not self._stack:
raise IndexError("Stack is empty")
diff = self._stack.pop()
if diff < 0:
self._min = self._min - diff

def top(self):
if not self._stack:
raise IndexError("Stack is empty")
diff = self._stack[-1]
if diff < 0:
return self._min
else:
return self._min + diff

def get_min(self):
if self._min is None:
raise IndexError("Stack is empty")
return self._min

实现方式 3:节点存储最小值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class StackNode:
def __init__(self, val, min_val):
self.val = val
self.min_val = min_val

class MinStackNode:
def __init__(self):
self._stack = []

def push(self, val):
if not self._stack:
min_val = val
else:
min_val = min(val, self._stack[-1].min_val)
self._stack.append(StackNode(val, min_val))

def pop(self):
if not self._stack:
raise IndexError("Stack is empty")
return self._stack.pop().val

def top(self):
if not self._stack:
raise IndexError("Stack is empty")
return self._stack[-1].val

def get_min(self):
if not self._stack:
raise IndexError("Stack is empty")
return self._stack[-1].min_val

队列(Queue)

什么是队列?

队列是一种先进先出(FIFO, First In First Out)的线性数据结构,在一端插入(队尾),在另一端删除(队首)。

队列的特点:

  • 先进先出:最先入队的元素最先出队
  • 两端操作:在队尾插入(enqueue),在队首删除(dequeue)
  • 有限大小:队列的大小通常是固定的

队列的操作:

  • **enqueue(x)**:将元素 x 插入队尾
  • **dequeue()**:删除并返回队首元素
  • **front()/peek()**:返回队首元素(不删除)
  • **empty()**:判断队列是否为空
  • **size()**:返回队列中元素的个数

队列的实现

使用数组实现队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class ArrayQueue:
def __init__(self, capacity=100):
self._queue = [None] * capacity
self._capacity = capacity
self._front = 0
self._rear = 0
self._size = 0

def enqueue(self, item):
if self._size >= self._capacity:
raise OverflowError("Queue is full")
self._queue[self._rear] = item
self._rear = (self._rear + 1) % self._capacity
self._size += 1

def dequeue(self):
if self.is_empty():
raise IndexError("Queue is empty")
item = self._queue[self._front]
self._queue[self._front] = None
self._front = (self._front + 1) % self._capacity
self._size -= 1
return item

def front(self):
if self.is_empty():
raise IndexError("Queue is empty")
return self._queue[self._front]

def is_empty(self):
return self._size == 0

def size(self):
return self._size

使用链表实现队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next

class LinkedQueue:
def __init__(self):
self._head = None
self._tail = None
self._size = 0

def enqueue(self, item):
new_node = ListNode(item)
if self._tail is None:
self._head = self._tail = new_node
else:
self._tail.next = new_node
self._tail = new_node
self._size += 1

def dequeue(self):
if self.is_empty():
raise IndexError("Queue is empty")
val = self._head.val
self._head = self._head.next
if self._head is None:
self._tail = None
self._size -= 1
return val

def front(self):
if self.is_empty():
raise IndexError("Queue is empty")
return self._head.val

def is_empty(self):
return self._head is None

def size(self):
return self._size

Python 内置的队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from collections import deque

# 使用 deque 作为队列(双端队列)
queue = deque()

# enqueue
queue.append(1)
queue.append(2)
queue.append(3)

# dequeue
front = queue.popleft() # 返回 1

# front
front = queue[0] # 返回 2,不删除

# empty
is_empty = len(queue) == 0

双端队列(Deque)

双端队列允许在两端进行插入和删除操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from collections import deque

deque_obj = deque()

# 在队首插入
deque_obj.appendleft(1)

# 在队尾插入
deque_obj.append(2)

# 在队首删除
left = deque_obj.popleft()

# 在队尾删除
right = deque_obj.pop()

优先队列(Priority Queue)

优先队列是一种特殊的队列,元素按优先级出队。

使用堆实现优先队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import heapq

class PriorityQueue:
def __init__(self):
self._heap = []
self._index = 0 # 用于处理优先级相同时的比较

def push(self, item, priority):
heapq.heappush(self._heap, (priority, self._index, item))
self._index += 1

def pop(self):
if self.is_empty():
raise IndexError("Priority queue is empty")
_, _, item = heapq.heappop(self._heap)
return item

def is_empty(self):
return len(self._heap) == 0

def size(self):
return len(self._heap)

队列的应用

1. 层序遍历二叉树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def level_order_traversal(root):
if not root:
return []

result = []
queue = [root]

while queue:
level = []
size = len(queue)

for _ in range(size):
node = queue.pop(0)
level.append(node.val)

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

result.append(level)

return result

2. BFS(广度优先搜索)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def bfs(graph, start):
"""图的广度优先搜索"""
visited = set()
queue = [start]
result = []

while queue:
node = queue.pop(0)
if node not in visited:
visited.add(node)
result.append(node)

for neighbor in graph[node]:
if neighbor not in visited:
queue.append(neighbor)

return result

3. 滑动窗口最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def max_sliding_window(nums, k):
"""使用双端队列实现滑动窗口最大值"""
if not nums or k == 0:
return []

deque_obj = []
result = []

for i in range(len(nums)):
# 移除窗口外的元素
while deque_obj and deque_obj[0] < i - k + 1:
deque_obj.pop(0)

# 移除小于当前元素的元素(保持单调递减)
while deque_obj and nums[deque_obj[-1]] < nums[i]:
deque_obj.pop()

deque_obj.append(i)

# 窗口形成后,记录最大值
if i >= k - 1:
result.append(nums[deque_obj[0]])

return result

排序算法

排序算法分类

按稳定性分类:

  • 稳定排序:相同元素在排序后的相对位置不变(冒泡、插入、归并、计数、基数)
  • 不稳定排序:相同元素在排序后的相对位置可能改变(选择、快速、堆)

按时间复杂度分类:

  • **O(n²)**:冒泡、选择、插入
  • **O(n log n)**:快速、归并、堆
  • **O(n)**:计数、基数、桶

按空间复杂度分类:

  • 原地排序:O(1)(冒泡、选择、插入、快速、堆)
  • 非原地排序:O(n)(归并、计数、基数、桶)

冒泡排序(Bubble Sort)

原理: 重复遍历数组,比较相邻元素,如果顺序错误就交换。

时间复杂度: O(n²)
空间复杂度: O(1)
稳定性: 稳定

1
2
3
4
5
6
7
8
9
10
11
12
def bubble_sort(arr):
n = len(arr)
for i in range(n):
swapped = False
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
# 如果没有交换,说明已经有序
if not swapped:
break
return arr

优化版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def bubble_sort_optimized(arr):
n = len(arr)
for i in range(n):
swapped = False
last_swap = n - 1
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
last_swap = j
if not swapped:
break
n = last_swap + 1
return arr

选择排序(Selection Sort)

原理: 每次找到未排序部分的最小元素,放到已排序部分的末尾。

时间复杂度: O(n²)
空间复杂度: O(1)
稳定性: 不稳定

1
2
3
4
5
6
7
8
9
def selection_sort(arr):
n = len(arr)
for i in range(n):
min_idx = i
for j in range(i + 1, n):
if arr[j] < arr[min_idx]:
min_idx = j
arr[i], arr[min_idx] = arr[min_idx], arr[i]
return arr

插入排序(Insertion Sort)

原理: 将元素逐个插入到已排序部分的正确位置。

时间复杂度: O(n²),最好情况 O(n)
空间复杂度: O(1)
稳定性: 稳定

1
2
3
4
5
6
7
8
9
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
return arr

二分插入排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def binary_insertion_sort(arr):
def binary_search(arr, val, start, end):
while start < end:
mid = (start + end) // 2
if arr[mid] < val:
start = mid + 1
else:
end = mid
return start

for i in range(1, len(arr)):
key = arr[i]
pos = binary_search(arr, key, 0, i)
arr[pos + 1:i + 1] = arr[pos:i]
arr[pos] = key
return arr

快速排序(Quick Sort)

原理: 选择一个基准元素,将数组分成两部分,小于基准的放左边,大于基准的放右边,递归处理两部分。

时间复杂度: 平均 O(n log n),最坏 O(n²)
空间复杂度: 平均 O(log n),最坏 O(n)
稳定性: 不稳定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def quick_sort(arr, low=0, high=None):
if high is None:
high = len(arr) - 1

if low < high:
pi = partition(arr, low, high)
quick_sort(arr, low, pi - 1)
quick_sort(arr, pi + 1, high)
return arr

def partition(arr, low, high):
pivot = arr[high]
i = low - 1

for j in range(low, high):
if arr[j] < pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]

arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1

三路快排(处理重复元素):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def quick_sort_3way(arr, low=0, high=None):
if high is None:
high = len(arr) - 1

if low >= high:
return

lt, i, gt = low, low, high
pivot = arr[low]

while i <= gt:
if arr[i] < pivot:
arr[lt], arr[i] = arr[i], arr[lt]
lt += 1
i += 1
elif arr[i] > pivot:
arr[i], arr[gt] = arr[gt], arr[i]
gt -= 1
else:
i += 1

quick_sort_3way(arr, low, lt - 1)
quick_sort_3way(arr, gt + 1, high)
return arr

归并排序(Merge Sort)

原理: 将数组分成两半,递归排序,然后合并两个有序数组。

时间复杂度: O(n log n)
空间复杂度: O(n)
稳定性: 稳定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def merge_sort(arr):
if len(arr) <= 1:
return arr

mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])

return merge(left, right)

def merge(left, right):
result = []
i, j = 0, 0

while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1

result.extend(left[i:])
result.extend(right[j:])
return result

原地归并排序(优化空间):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def merge_sort_inplace(arr, low=0, high=None):
if high is None:
high = len(arr) - 1

if low < high:
mid = (low + high) // 2
merge_sort_inplace(arr, low, mid)
merge_sort_inplace(arr, mid + 1, high)
merge_inplace(arr, low, mid, high)
return arr

def merge_inplace(arr, low, mid, high):
left = arr[low:mid + 1]
right = arr[mid + 1:high + 1]

i, j, k = 0, 0, low

while i < len(left) and j < len(right):
if left[i] <= right[j]:
arr[k] = left[i]
i += 1
else:
arr[k] = right[j]
j += 1
k += 1

while i < len(left):
arr[k] = left[i]
i += 1
k += 1

while j < len(right):
arr[k] = right[j]
j += 1
k += 1

堆排序(Heap Sort)

原理: 构建最大堆,然后将堆顶元素(最大值)与末尾元素交换,调整堆,重复此过程。

时间复杂度: O(n log n)
空间复杂度: O(1)
稳定性: 不稳定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def heap_sort(arr):
n = len(arr)

# 构建最大堆
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)

# 逐个取出堆顶元素
for i in range(n - 1, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
heapify(arr, i, 0)

return arr

def heapify(arr, n, i):
largest = i
left = 2 * i + 1
right = 2 * i + 2

if left < n and arr[left] > arr[largest]:
largest = left

if right < n and arr[right] > arr[largest]:
largest = right

if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)

使用 heapq 模块:

1
2
3
4
5
6
import heapq

def heap_sort_builtin(arr):
heap = arr[:]
heapq.heapify(heap)
return [heapq.heappop(heap) for _ in range(len(heap))]

计数排序(Counting Sort)

原理: 统计每个元素出现的次数,然后按照计数结果输出。

时间复杂度: O(n + k),k 是数据范围
空间复杂度: O(k)
稳定性: 稳定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def counting_sort(arr, max_val=None):
if max_val is None:
max_val = max(arr) if arr else 0

count = [0] * (max_val + 1)

# 计数
for num in arr:
count[num] += 1

# 累计计数
for i in range(1, len(count)):
count[i] += count[i - 1]

# 输出
result = [0] * len(arr)
for i in range(len(arr) - 1, -1, -1):
count[arr[i]] -= 1
result[count[arr[i]]] = arr[i]

return result

基数排序(Radix Sort)

原理: 按照低位到高位的顺序,依次对每一位进行计数排序。

时间复杂度: O(d × (n + k)),d 是位数,k 是基数
空间复杂度: O(n + k)
稳定性: 稳定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def radix_sort(arr):
if not arr:
return arr

max_val = max(arr)
exp = 1

while max_val // exp > 0:
counting_sort_by_digit(arr, exp)
exp *= 10

return arr

def counting_sort_by_digit(arr, exp):
n = len(arr)
output = [0] * n
count = [0] * 10

# 计数
for i in range(n):
index = (arr[i] // exp) % 10
count[index] += 1

# 累计计数
for i in range(1, 10):
count[i] += count[i - 1]

# 输出
for i in range(n - 1, -1, -1):
index = (arr[i] // exp) % 10
output[count[index] - 1] = arr[i]
count[index] -= 1

for i in range(n):
arr[i] = output[i]

桶排序(Bucket Sort)

原理: 将数据分成若干个桶,对每个桶进行排序,然后合并。

时间复杂度: 平均 O(n + k),最坏 O(n²)
空间复杂度: O(n + k)
稳定性: 稳定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def bucket_sort(arr, bucket_count=10):
if not arr:
return arr

min_val = min(arr)
max_val = max(arr)
bucket_size = (max_val - min_val) / bucket_count + 1

buckets = [[] for _ in range(bucket_count)]

# 将元素分配到桶中
for num in arr:
index = int((num - min_val) / bucket_size)
buckets[index].append(num)

# 对每个桶进行排序
for bucket in buckets:
bucket.sort() # 可以使用其他排序算法

# 合并结果
result = []
for bucket in buckets:
result.extend(bucket)

return result

希尔排序(Shell Sort)

原理: 改进的插入排序,通过分组进行插入排序,逐步缩小间隔。

时间复杂度: 平均 O(n^1.3),最坏 O(n²)
空间复杂度: O(1)
稳定性: 不稳定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def shell_sort(arr):
n = len(arr)
gap = n // 2

while gap > 0:
for i in range(gap, n):
temp = arr[i]
j = i
while j >= gap and arr[j - gap] > temp:
arr[j] = arr[j - gap]
j -= gap
arr[j] = temp
gap //= 2

return arr

排序算法总结

算法 平均时间 最坏时间 空间复杂度 稳定性
冒泡排序 O(n²) O(n²) O(1) 稳定
选择排序 O(n²) O(n²) O(1) 不稳定
插入排序 O(n²) O(n²) O(1) 稳定
快速排序 O(n log n) O(n²) O(log n) 不稳定
归并排序 O(n log n) O(n log n) O(n) 稳定
堆排序 O(n log n) O(n log n) O(1) 不稳定
计数排序 O(n + k) O(n + k) O(k) 稳定
基数排序 O(d × n) O(d × n) O(n + k) 稳定
桶排序 O(n + k) O(n²) O(n + k) 稳定
希尔排序 O(n^1.3) O(n²) O(1) 不稳定

时间复杂度与空间复杂度

时间复杂度(Time Complexity)

时间复杂度是算法执行时间随输入规模增长的增长率。

常见的时间复杂度

1. O(1) - 常数时间复杂度

1
2
def get_first_element(arr):
return arr[0] # 无论数组多大,都是常数时间

2. O(log n) - 对数时间复杂度

1
2
3
4
5
6
7
8
9
10
11
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1

3. O(n) - 线性时间复杂度

1
2
3
4
5
def linear_search(arr, target):
for i, num in enumerate(arr):
if num == target:
return i
return -1

4. O(n log n) - 线性对数时间复杂度

1
2
3
4
5
6
7
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right) # 归并排序

5. O(n²) - 平方时间复杂度

1
2
3
4
5
6
7
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr

6. O(n³) - 立方时间复杂度

1
2
3
4
5
6
7
8
def matrix_multiplication(A, B):
n = len(A)
C = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
for k in range(n):
C[i][j] += A[i][k] * B[k][j]
return C

7. O(2ⁿ) - 指数时间复杂度

1
2
3
4
def fibonacci_recursive(n):
if n <= 1:
return n
return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)

8. O(n!) - 阶乘时间复杂度

1
2
3
4
5
6
7
8
9
def generate_permutations(arr):
if len(arr) <= 1:
return [arr]
result = []
for i in range(len(arr)):
rest = arr[:i] + arr[i+1:]
for perm in generate_permutations(rest):
result.append([arr[i]] + perm)
return result

时间复杂度的计算方法

1. 单个循环

1
2
3
4
# O(n)
for i in range(n):
# 常数时间操作
pass

2. 嵌套循环

1
2
3
4
5
# O(n²)
for i in range(n):
for j in range(n):
# 常数时间操作
pass

3. 循环中的循环

1
2
3
4
5
6
# O(n²)
for i in range(n):
for j in range(i, n):
# 常数时间操作
pass
# 总次数:n + (n-1) + ... + 1 = n(n+1)/2 = O(n²)

4. 递归调用

1
2
3
4
5
6
# O(2ⁿ)
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# T(n) = T(n-1) + T(n-2) + O(1) ≈ O(2ⁿ)

5. 分治算法

1
2
3
4
5
6
7
8
9
# O(n log n)
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid]) # T(n/2)
right = merge_sort(arr[mid:]) # T(n/2)
return merge(left, right) # O(n)
# T(n) = 2T(n/2) + O(n) = O(n log n)

最好、平均、最坏时间复杂度

1. 最好情况时间复杂度(Best Case)

  • 算法在最理想情况下的时间复杂度
  • 例如:冒泡排序最好情况 O(n)(已经有序)

2. 平均情况时间复杂度(Average Case)

  • 算法在所有可能输入情况下的平均时间复杂度
  • 例如:快速排序平均情况 O(n log n)

3. 最坏情况时间复杂度(Worst Case)

  • 算法在最坏情况下的时间复杂度
  • 例如:快速排序最坏情况 O(n²)(每次都选到最大或最小元素)

空间复杂度(Space Complexity)

空间复杂度是算法使用的额外内存空间随输入规模增长的增长率。

常见的空间复杂度

1. O(1) - 常数空间复杂度

1
2
def swap(a, b):
a, b = b, a # 只使用常数个额外变量

2. O(n) - 线性空间复杂度

1
2
3
4
5
def copy_array(arr):
result = [0] * len(arr) # 创建长度为 n 的数组
for i in range(len(arr)):
result[i] = arr[i]
return result

3. O(n²) - 平方空间复杂度

1
2
def create_matrix(n):
return [[0] * n for _ in range(n)] # n × n 的矩阵

4. O(log n) - 对数空间复杂度

1
2
3
4
5
6
7
8
9
10
11
def binary_search_recursive(arr, target, left, right):
if left > right:
return -1
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
return binary_search_recursive(arr, target, mid + 1, right)
else:
return binary_search_recursive(arr, target, left, mid - 1)
# 递归深度为 log n,栈空间为 O(log n)

空间复杂度的计算方法

1. 变量空间

1
2
3
4
5
6
# O(1)
def sum_array(arr):
total = 0 # 1 个变量
for num in arr:
total += num
return total

2. 数组空间

1
2
3
4
5
6
# O(n)
def reverse_array(arr):
result = [0] * len(arr) # 长度为 n 的数组
for i in range(len(arr)):
result[i] = arr[len(arr) - 1 - i]
return result

3. 递归栈空间

1
2
3
4
5
6
# O(n)
def factorial_recursive(n):
if n <= 1:
return 1
return n * factorial_recursive(n - 1)
# 递归深度为 n,栈空间为 O(n)

4. 哈希表空间

1
2
3
4
5
6
7
# O(n)
def count_frequency(arr):
freq = {}
for num in arr:
freq[num] = freq.get(num, 0) + 1
return freq
# 最坏情况下,所有元素都不同,需要 O(n) 空间

复杂度分析示例

示例 1:数组求和

1
2
3
4
5
6
7
def sum_array(arr):
total = 0
for num in arr:
total += num
return total
# 时间复杂度:O(n)
# 空间复杂度:O(1)

示例 2:冒泡排序

1
2
3
4
5
6
7
8
9
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
# 时间复杂度:O(n²)
# 空间复杂度:O(1)

示例 3:归并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)

def merge(left, right):
result = []
i, j = 0, 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
# 时间复杂度:O(n log n)
# 空间复杂度:O(n)

示例 4:快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def quick_sort(arr, low=0, high=None):
if high is None:
high = len(arr) - 1
if low < high:
pi = partition(arr, low, high)
quick_sort(arr, low, pi - 1)
quick_sort(arr, pi + 1, high)
return arr

def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] < pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
# 时间复杂度:平均 O(n log n),最坏 O(n²)
# 空间复杂度:平均 O(log n),最坏 O(n)

常见面试题

栈相关面试题

1. 实现一个栈,支持 O(1) 时间复杂度的 getMin() 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)
else:
self._min_stack.append(self._min_stack[-1])

def pop(self):
if not self._stack:
raise IndexError("Stack is empty")
self._stack.pop()
self._min_stack.pop()

def top(self):
return self._stack[-1] if self._stack else None

def get_min(self):
return self._min_stack[-1] if self._min_stack else None

2. 用两个栈实现队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class QueueWithStacks:
def __init__(self):
self._stack1 = []
self._stack2 = []

def enqueue(self, x):
self._stack1.append(x)

def dequeue(self):
if not self._stack2:
while self._stack1:
self._stack2.append(self._stack1.pop())
if not self._stack2:
raise IndexError("Queue is empty")
return self._stack2.pop()

def front(self):
if not self._stack2:
while self._stack1:
self._stack2.append(self._stack1.pop())
if not self._stack2:
raise IndexError("Queue is empty")
return self._stack2[-1]

3. 用两个队列实现栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from collections import deque

class StackWithQueues:
def __init__(self):
self._queue1 = deque()
self._queue2 = deque()

def push(self, x):
self._queue1.append(x)

def pop(self):
if not self._queue1:
raise IndexError("Stack is empty")
while len(self._queue1) > 1:
self._queue2.append(self._queue1.popleft())
result = self._queue1.popleft()
self._queue1, self._queue2 = self._queue2, self._queue1
return result

def top(self):
if not self._queue1:
raise IndexError("Stack is empty")
return self._queue1[-1]

def empty(self):
return len(self._queue1) == 0

4. 有效的括号

1
2
3
4
5
6
7
8
9
10
11
12
def is_valid(s):
stack = []
mapping = {')': '(', ']': '[', '}': '{'}

for char in s:
if char in mapping:
if not stack or stack.pop() != mapping[char]:
return False
else:
stack.append(char)

return len(stack) == 0

5. 每日温度(下一个更大元素)

1
2
3
4
5
6
7
8
9
10
11
def daily_temperatures(temperatures):
result = [0] * len(temperatures)
stack = [] # 存储索引

for i in range(len(temperatures)):
while stack and temperatures[stack[-1]] < temperatures[i]:
index = stack.pop()
result[index] = i - index
stack.append(i)

return result

队列相关面试题

1. 滑动窗口最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from collections import deque

def max_sliding_window(nums, k):
if not nums or k == 0:
return []

deque_obj = deque()
result = []

for i in range(len(nums)):
# 移除窗口外的元素
while deque_obj and deque_obj[0] < i - k + 1:
deque_obj.popleft()

# 移除小于当前元素的元素(保持单调递减)
while deque_obj and nums[deque_obj[-1]] < nums[i]:
deque_obj.pop()

deque_obj.append(i)

# 窗口形成后,记录最大值
if i >= k - 1:
result.append(nums[deque_obj[0]])

return result

2. 设计循环队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class CircularQueue:
def __init__(self, k):
self._queue = [None] * k
self._capacity = k
self._front = 0
self._rear = 0
self._size = 0

def enqueue(self, value):
if self.is_full():
return False
self._queue[self._rear] = value
self._rear = (self._rear + 1) % self._capacity
self._size += 1
return True

def dequeue(self):
if self.is_empty():
return False
self._queue[self._front] = None
self._front = (self._front + 1) % self._capacity
self._size -= 1
return True

def front(self):
if self.is_empty():
return -1
return self._queue[self._front]

def rear(self):
if self.is_empty():
return -1
return self._queue[(self._rear - 1) % self._capacity]

def is_empty(self):
return self._size == 0

def is_full(self):
return self._size == self._capacity

排序算法面试题

1. 排序算法的稳定性是什么?为什么重要?

答案:

  • 稳定性:相同元素在排序后的相对位置不变
  • 重要性
    • 多关键字排序:先按一个关键字排序,再按另一个关键字排序
    • 保持原有顺序:对于相同元素,保持它们在原数组中的顺序
    • 例如:按姓名排序后再按年龄排序,年龄相同时保持姓名顺序

2. 快速排序和归并排序的区别?

答案:

特性 快速排序 归并排序
时间复杂度 平均 O(n log n),最坏 O(n²) 始终 O(n log n)
空间复杂度 平均 O(log n),最坏 O(n) O(n)
稳定性 不稳定 稳定
实现方式 原地排序(in-place) 需要额外空间
最坏情况 已经有序或逆序 始终相同
适用场景 大数据量,随机数据 需要稳定性,小数据量

3. 什么情况下使用计数排序?

答案:

  • 数据范围小:数据范围远小于数据个数(如年龄、成绩)
  • 非负整数:数据是非负整数
  • 需要稳定性:需要稳定排序
  • 时间复杂度:需要 O(n + k) 的时间复杂度

示例:

1
2
3
4
5
# 适合计数排序:0-100 的年龄数据
ages = [25, 30, 25, 28, 30, 25]

# 不适合计数排序:数据范围太大(如 ID)
ids = [1000000, 2000000, 3000000, ...]

4. 如何选择排序算法?

答案:

场景 推荐算法 原因
小数据量(< 50) 插入排序 简单、常数因子小
大数据量,随机数据 快速排序 平均性能最好
需要稳定性 归并排序 稳定且时间复杂度稳定
数据范围小 计数排序 O(n + k) 时间复杂度
需要原地排序 堆排序 O(1) 空间复杂度
部分有序 插入排序 最好情况 O(n)

5. 如何优化快速排序?

答案:

  1. 随机选择基准:避免最坏情况
  2. 三路快排:处理重复元素
  3. 插入排序优化:小数组使用插入排序
  4. 尾递归优化:减少栈空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def quick_sort_optimized(arr, low=0, high=None):
if high is None:
high = len(arr) - 1

# 小数组使用插入排序
if high - low < 10:
return insertion_sort(arr[low:high+1])

# 随机选择基准
import random
pivot_idx = random.randint(low, high)
arr[pivot_idx], arr[high] = arr[high], arr[pivot_idx]

if low < high:
pi = partition(arr, low, high)
quick_sort_optimized(arr, low, pi - 1)
quick_sort_optimized(arr, pi + 1, high)

return arr

复杂度分析面试题

1. 如何计算递归算法的时间复杂度?

答案:
使用递推公式或主定理(Master Theorem)。

示例 1:归并排序

1
2
3
4
5
T(n) = 2T(n/2) + O(n)
使用主定理:a = 2, b = 2, f(n) = O(n)
log_b(a) = log_2(2) = 1
f(n) = O(n^1)
所以 T(n) = O(n log n)

示例 2:二分查找

1
2
3
4
5
T(n) = T(n/2) + O(1)
使用主定理:a = 1, b = 2, f(n) = O(1)
log_b(a) = log_2(1) = 0
f(n) = O(n^0)
所以 T(n) = O(log n)

2. 如何计算递归算法的空间复杂度?

答案:
空间复杂度 = 递归深度 × 每次递归的空间复杂度

示例:

1
2
3
4
5
6
7
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
# 递归深度:O(n)
# 每次递归空间:O(1)
# 总空间复杂度:O(n)

3. 时间复杂度 O(n) 和 O(n log n) 哪个更好?

答案:

  • O(n) 更好:线性时间比线性对数时间快
  • 但是:考虑常数因子,O(n log n) 的常数可能更小
  • 实际场景:当 n 很大时,O(n log n) 和 O(n) 的差距很明显

总结

核心要点:

  1. 栈(Stack)

    • LIFO 结构,后进先出
    • 应用:表达式求值、括号匹配、递归调用
    • 最小栈可以在 O(1) 时间内获取最小值
  2. 队列(Queue)

    • FIFO 结构,先进先出
    • 应用:BFS、层序遍历、任务调度
    • 双端队列和优先队列是重要变体
  3. 排序算法

    • 稳定排序:冒泡、插入、归并、计数、基数
    • O(n log n):快速、归并、堆
    • O(n):计数、基数、桶(特定条件下)
  4. 复杂度分析

    • 时间复杂度:算法执行时间的增长率
    • 空间复杂度:算法使用内存的增长率
    • 需要分析最好、平均、最坏情况

面试重点:

  • 栈和队列的实现和应用
  • 最小栈的实现(多种方法)
  • 各种排序算法的特点、时间空间复杂度
  • 排序算法的稳定性
  • 如何选择排序算法
  • 时间复杂度和空间复杂度的计算

实际应用:

在实际项目中:

  • :用于表达式解析、函数调用、撤销操作
  • 队列:用于任务调度、消息队列、BFS 算法
  • 排序:根据数据特点选择最合适的排序算法
  • 复杂度分析:评估算法性能,优化代码

参考资料:

  • 《算法导论》(Introduction to Algorithms)
  • 《数据结构与算法分析》(Data Structures and Algorithm Analysis)
  • 《编程珠玑》(Programming Pearls)
  • LeetCode 算法专题