一:分析算法的时间复杂度
时间复杂度是计算机科学中的一个概念,用于描述算法在输入规模增长时所需时间的增长情况。时间复杂度通常使用大 O 符号来表示。分析时间复杂度的目的是了解算法的效率,并预测其在大规模数据下的表现。
1. 基本操作的时间复杂度
基本操作包括常数时间操作,如赋值、算术运算、比较、数组访问等。每个基本操作的时间复杂度通常是 O(1)。
2. 分析循环
循环是影响算法时间复杂度的主要结构之一。不同类型的循环对时间复杂度的影响如下:
-
单层循环:如果一个循环从 1 迭代到 n,其时间复杂度是 O(n)。
for i in range(n):
# 常数时间操作 O(1)
-
嵌套循环:嵌套循环的总时间复杂度是各层循环次数的乘积。
for i in range(n): # O(n)
for j in range(m): # O(m)
# 常数时间操作 O(1)
上述代码的时间复杂度是 O(n×m)。
3. 分析递归
递归算法的时间复杂度分析通常通过递归方程来进行。
-
简单递归:如斐波那契数列。
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
-
分治法:如归并排序和快速排序。
4. 分析条件语句
条件语句的时间复杂度取决于每个分支的复杂度和被执行的次数。
if condition:
# 分支 A 操作
else:
# 分支 B 操作
线性搜索:遍历一个长度为 �n 的列表,时间复杂度为 O(n)。
def linear_search(arr, target):
for item in arr:
if item == target:
return True
return False
二分搜索:在一个有序数组中查找目标元素,时间复杂度为O(logn)。
def binary_search(arr, target):
low = 0
high = len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return True
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return False
快速排序:最坏情况下时间复杂度为O(n^2),但平均情况为 O(nlogn)。
def quicksort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + middle + quicksort(right)
二:常见算法时间复杂度及代码举例
以下是一些常见算法的时间复杂度分析及对应的代码示例:
1. 线性搜索(Linear Search)
时间复杂度: O(n)
线性搜索遍历整个数组,查找目标元素。
def linear_search(arr, target):
for i in range(len(arr)):
if arr[i] == target:
return i
return -1
# 示例
arr = [2, 3, 4, 10, 40]
target = 10
result = linear_search(arr, target)
2. 二分搜索(Binary Search)
时间复杂度: O(logn)
二分搜索在一个有序数组中查找目标元素。
def binary_search(arr, target):
low, high = 0, len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1
# 示例
arr = [2, 3, 4, 10, 40]
target = 10
result = binary_search(arr, target)
3. 冒泡排序(Bubble Sort)
时间复杂度: 最坏情况 O(n^2),平均情况 )O(n^2)
冒泡排序逐步将最大或最小元素移动到正确位置。
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]
# 示例
arr = [64, 34, 25, 12, 22, 11, 90]
bubble_sort(arr)
4. 快速排序(Quick Sort)
时间复杂度: 最坏情况 O(n^2),平均情况 O(nlogn)
快速排序通过分治法将数组分为较小和较大两部分,再递归排序。
def quicksort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + middle + quicksort(right)
# 示例
arr = [3, 6, 8, 10, 1, 2, 1]
sorted_arr = quicksort(arr)
5. 归并排序(Merge Sort)
时间复杂度:O(nlogn)
归并排序也使用分治法,将数组分为两半分别排序,再合并。
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
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
# 示例
arr = [12, 11, 13, 5, 6, 7]
sorted_arr = merge_sort(arr)
6. Dijkstra算法(Dijkstra's Algorithm)
时间复杂度:O((V+E)logV),其中 V 是顶点数,E 是边数
Dijkstra算法用于计算单源最短路径。
import heapq
def dijkstra(graph, start):
queue = []
heapq.heappush(queue, (0, start))
distances = {vertex: float('infinity') for vertex in graph}
distances[start] = 0
while queue:
current_distance, current_vertex = heapq.heappop(queue)
if current_distance > distances[current_vertex]:
continue
for neighbor, weight in graph[current_vertex].items():
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(queue, (distance, neighbor))
return distances
# 示例
graph = {
'A': {'B': 1, 'C': 4},
'B': {'A': 1, 'C': 2, 'D': 5},
'C': {'A': 4, 'B': 2, 'D': 1},
'D': {'B': 5, 'C': 1}
}
start_vertex = 'A'
distances = dijkstra(graph, start_vertex)
三:递归、贪心、分治、动态规划、回溯和分支限界、随机化算法
1. 递归算法(Recursion)
时间复杂度: 取决于具体问题,一般为)O(2^n) 或O(n!) 等
递归算法通过函数调用自身来解决问题,通常用于分解问题。
示例: 计算斐波那契数列
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
# 示例
n = 5
result = fibonacci(n)
2. 贪心算法(Greedy Algorithm)
时间复杂度: 取决于具体问题,一般为 O(nlogn) 或O(n)
贪心算法通过每一步选择当前最优解来求解问题,适用于局部最优能导致全局最优的情况。
示例: 活动选择问题
def activity_selection(start, end):
n = len(start)
activities = sorted(zip(start, end), key=lambda x: x[1])
selected = []
current_end = 0
for s, e in activities:
if s >= current_end:
selected.append((s, e))
current_end = e
return selected
# 示例
start = [1, 3, 0, 5, 8, 5]
end = [2, 4, 6, 7, 9, 9]
selected_activities = activity_selection(start, end)
3. 分治算法(Divide and Conquer)
时间复杂度: 取决于具体问题,一般为O(nlogn)
分治算法将问题分解为更小的子问题,递归解决,然后合并结果。
示例: 归并排序
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
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
# 示例
arr = [12, 11, 13, 5, 6, 7]
sorted_arr = merge_sort(arr)
4. 动态规划(Dynamic Programming)
时间复杂度: 取决于具体问题,一般为O(n^2) 或O(n*m) 等
动态规划通过保存子问题的解来避免重复计算,适用于具有重叠子问题和最优子结构性质的问题。
示例: 背包问题
def knapsack(values, weights, W):
n = len(values)
dp = [[0 for _ in range(W + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(W + 1):
if weights[i-1] <= w:
dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1])
else:
dp[i][w] = dp[i-1][w]
return dp[n][W]
# 示例
values = [60, 100, 120]
weights = [10, 20, 30]
W = 50
max_value = knapsack(values, weights, W)
5. 回溯算法(Backtracking)
时间复杂度: 取决于具体问题,一般为O(2^n) 或O(n!)
回溯算法通过尝试和撤销选择来寻找所有可能的解。
示例: N皇后问题
def solve_n_queens(n):
def is_safe(board, row, col):
for i in range(row):
if board[i] == col or \
board[i] - i == col - row or \
board[i] + i == col + row:
return False
return True
def solve(board, row):
if row == n:
result.append(board[:])
return
for col in range(n):
if is_safe(board, row, col):
board[row] = col
solve(board, row + 1)
result = []
solve([-1] * n, 0)
return result
# 示例
n = 4
solutions = solve_n_queens(n)
6. 分支限界(Branch and Bound)
时间复杂度: 取决于具体问题,一般为 O(2^n)
分支限界通过剪枝减少搜索空间,适用于组合优化问题。
示例: 旅行商问题
import sys
def travel_salesman_problem(graph, start):
n = len(graph)
all_visited = (1 << n) - 1
memo = [[-1] * n for _ in range(1 << n)]
def tsp(mask, pos):
if mask == all_visited:
return graph[pos][start]
if memo[mask][pos] != -1:
return memo[mask][pos]
ans = sys.maxsize
for city in range(n):
if mask & (1 << city) == 0:
new_ans = graph[pos][city] + tsp(mask | (1 << city), city)
ans = min(ans, new_ans)
memo[mask][pos] = ans
return ans
return tsp(1 << start, start)
# 示例
graph = [
[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]
]
start = 0
min_cost = travel_salesman_problem(graph, start)
7. 随机化算法(Randomized Algorithm)
时间复杂度: 取决于具体问题,一般为O(nlogn)
随机化算法通过引入随机因素来提高平均情况下的性能或简化算法设计。
示例: 随机化快速排序
import random
def randomized_quicksort(arr):
if len(arr) <= 1:
return arr
pivot = arr[random.randint(0, len(arr) - 1)]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return randomized_quicksort(left) + middle + randomized_quicksort(right)
# 示例
arr = [3, 6, 8, 10, 1, 2, 1]
sorted_arr = randomized_quicksort(arr)