令人惊艳的高效算法盘点(附示例)

news/2024/10/21 9:28:07/

令人惊艳的高效算法盘点(附示例)

在计算机科学领域,算法是解决问题的基石。有些算法,因为其高效性和惊人表现,令人瞩目。本文将为你介绍一些令人惊艳的高效算法,让我们一起来领略这些算法的魅力吧!
在这里插入图片描述

1. 快速排序(Quick Sort)

快速排序是一种高效的排序算法,由英国计算机科学家托尼·霍尔(Tony Hoare)于1960年代发明。快速排序使用分治策略(Divide-and-Conquer),将一个大的问题分解成若干个小问题来解决。其主要步骤如下:

  1. 选择基准元素(Pivot)。
  2. 将数组中的元素分为两部分,一部分是小于基准元素的,另一部分是大于基准元素的。
  3. 对两个子数组分别进行快速排序。
  4. 合并排序后的子数组。

快速排序的平均时间复杂度为O(n*log(n)),在实际应用中表现优异。它不需要额外的存储空间,因此在空间复杂度方面也表现出色。

这是一个快速排序的Python演示:

def quick_sort(arr):"""快速排序函数:param arr: 待排序数组:return: 排序后的数组"""if len(arr) < 2:return arrpivot = arr[0]  # 取第一个元素作为基准值left = [i for i in arr[1:] if i <= pivot]  # 找出小于等于基准值的元素right = [i for i in arr[1:] if i > pivot]  # 找出大于基准值的元素return quick_sort(left) + [pivot] + quick_sort(right)  # 递归调用并合并结果# 测试
arr = [3, 5, 4, 2, 1]
sorted_arr = quick_sort(arr)
print(sorted_arr)  # 输出 [1, 2, 3, 4, 5]

在快速排序中,我们选择一个基准值(通常是数组的第一个元素),将数组分为两部分,左边的元素都小于等于基准值,右边的元素都大于基准值。然后递归地对左右两部分进行排序,最终合并起来就得到了排序后的数组。

2. 归并排序(Merge Sort)

归并排序是另一种高效的排序算法,由约翰·冯·诺依曼(John von Neumann)于1945年发明。归并排序同样采用分治策略。其主要步骤如下:

  1. 将数组分成两个相等的子数组。
  2. 对子数组进行归并排序。
  3. 将排序后的子数组合并成一个有序数组。

归并排序的时间复杂度为O(n*log(n)),与快速排序相同。不过,归并排序需要额外的存储空间,空间复杂度为O(n)。

这是一个归并排序的Python演示:

def merge_sort(arr):"""归并排序函数:param arr: 待排序数组:return: 排序后的数组"""if len(arr) < 2:return arrmid = len(arr) // 2  # 找到数组中间的位置left_arr = arr[:mid]  # 将数组分为左右两部分right_arr = arr[mid:]# 递归调用归并排序函数left_sorted = merge_sort(left_arr)right_sorted = merge_sort(right_arr)# 合并两个已排好序的子数组sorted_arr = []i, j = 0, 0while i < len(left_sorted) and j < len(right_sorted):if left_sorted[i] <= right_sorted[j]:sorted_arr.append(left_sorted[i])i += 1else:sorted_arr.append(right_sorted[j])j += 1sorted_arr += left_sorted[i:]  # 将剩余的元素加入到已排好序的数组中sorted_arr += right_sorted[j:]return sorted_arr# 测试
arr = [3, 5, 4, 2, 1]
sorted_arr = merge_sort(arr)
print(sorted_arr)  # 输出 [1, 2, 3, 4, 5]

在归并排序中,我们将数组递归地划分为两个子数组,直到每个子数组只包含一个元素。然后将相邻的子数组合并并排序。最终,所有子数组都被合并成一个有序的数组。归并排序具有稳定性和适用于链表等数据结构的特点。

3. 动态规划(Dynamic Programming)

动态规划是一种解决优化问题的通用方法,尤其适用于具有重叠子问题和最优子结构的问题。动态规划通过将问题分解为若干个子问题来解决,同时将子问题的答案存储起来,避免了重复计算。

经典的动态规划问题包括背包问题(Knapsack Problem)、最长公共子序列(Longest Common Subsequence)和最短路径问题(Shortest Path Problem)。动态规划在很多领域都有着广泛的应用,例如人工智能、运筹学、生物信息学等。

这是一个动态规划的Python演示。下面以背包问题为例进行演示:

def knapsack(weights, values, capacity):"""动态规划求解背包问题:param weights: 物品重量列表:param values: 物品价值列表:param capacity: 背包容量:return: 最大价值和及所选物品的编号列表"""n = len(weights)  # 物品数量dp = [[0] * (capacity + 1) for _ in range(n + 1)]  # 创建动态规划表for i in range(1, n + 1):for j in range(1, capacity + 1):if j < weights[i - 1]:  # 当前物品放不进背包dp[i][j] = dp[i - 1][j]else:  # 可以放进背包,比较放和不放的价值大小,选择最优解dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])# 回溯查找所选物品的编号selected_items = []j = capacityfor i in range(n, 0, -1):if dp[i][j] > dp[i - 1][j]:selected_items.append(i - 1)j -= weights[i - 1]return dp[n][capacity], selected_items[::-1]# 测试
weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
capacity = 8
max_value, selected_items = knapsack(weights, values, capacity)
print(max_value)  # 输出 11
print(selected_items)  # 输出 [0, 2, 3]

在动态规划中,我们将问题分解为一系列子问题,并存储每个子问题的解。然后通过组合这些子问题的解来获得原始问题的解。在背包问题中,我们创建一个二维表,其中行表示物品,列表示容量。我们使用递归公式来填充表格,该公式基于以下两种情况:当前物品可以放入背包或不可放入背包。最后,我们回溯表格以找到所选物品的编号列表。

4. 深度优先搜索(Depth-First Search, DFS)和广度优先搜索(Breadth-First Search, BFS)

深度优先搜索和广度优先搜索是图遍历算法,分别采用栈(Stack)和队列(Queue)作为辅助数据结构。他们的时间复杂度分别为O(V+E),其中V是顶点数,E是边数。

深度优先搜索从图的某一顶点出发,沿着一条路径深入遍历,直至无法继续为止,然后回溯到上一个顶点,继续遍历其他路径。广度优先搜索从图的某一顶点出发,先访问所有相邻的顶点,然后按照访问顺序继续访问下一层的顶点。

这两种算法在图论、人工智能和网络科学等领域有着广泛的应用。

下面是深度优先搜索和广度优先搜索的Python演示。首先是深度优先搜索DFS:

def dfs(graph, start, visited=None):"""深度优先搜索函数:param graph: 图的邻接表表示:param start: 起始节点:param visited: 记录已访问节点的集合:return: 从起始节点开始遍历得到的节点序列"""if visited is None:visited = set()visited.add(start)for next_node in graph[start] - visited:dfs(graph, next_node, visited)return visited# 测试
graph = {'A': set(['B', 'C']),'B': set(['A', 'D', 'E']),'C': set(['A', 'F']),'D': set(['B']),'E': set(['B', 'F']),'F': set(['C', 'E'])}
print(dfs(graph, 'A'))

在DFS中,我们递归地访问每个未访问的邻居节点,并将其标记为已访问。当无法访问新的未访问邻居节点时,我们回溯并继续访问其他未访问的节点。最终,我们得到从起始节点开始的遍历路径。

接下来是广度优先搜索BFS:

from collections import dequedef bfs(graph, start):"""广度优先搜索函数:param graph: 图的邻接表表示:param start: 起始节点:return: 从起始节点开始遍历得到的节点序列"""visited, queue = set(), deque([start])visited.add(start)while queue:node = queue.popleft()for next_node in graph[node] - visited:visited.add(next_node)queue.append(next_node)return visited# 测试
graph = {'A': set(['B', 'C']),'B': set(['A', 'D', 'E']),'C': set(['A', 'F']),'D': set(['B']),'E': set(['B', 'F']),'F': set(['C', 'E'])}
print(bfs(graph, 'A'))

在BFS中,我们使用一个队列来存储当前要访问的节点,并依次访问每个节点的未访问邻居节点。当我们访问新的邻居节点时,我们将其标记为已访问并将其添加到队列中。最终,我们得到从起始节点开始的遍历路径。

5. Dijkstra算法(迪杰斯特拉算法)

Dijkstra算法是一种解决单源最短路径问题的高效算法,由荷兰计算机科学家艾兹格·戴科斯彻(Edsger W. Dijkstra)于1956年发明。Dijkstra算法可以在有向图和无向图中找到从源顶点到所有其他顶点的最短路径。其主要步骤如下:

  1. 初始化距离表,将源顶点的距离设为0,其他顶点的距离设为无穷大。
  2. 从距离表中选择距离最小的未访问顶点。
  3. 更新该顶点的所有相邻顶点的距离。
  4. 重复步骤2和3,直到所有顶点都被访问。

Dijkstra算法的时间复杂度为O(V^2),但使用优先队列(Priority Queue)的实现可以将时间复杂度降低到O(V+E*log(V))。

这是一个Dijkstra算法的Python演示:

import heapqdef dijkstra(graph, start):"""Dijkstra算法函数:param graph: 图的邻接表表示:param start: 起始节点:return: 从起始节点到各个节点的最短路径和距离"""distances = {node: float('inf') for node in graph}  # 初始化距离为无穷大distances[start] = 0  # 起始节点到自身的距离为0pq = [(0, start)]  # 使用堆来存储当前待处理的节点while pq:(min_distance, current_node) = heapq.heappop(pq)  # 弹出当前最小距离的节点if min_distance > distances[current_node]:  # 如果已经处理过该节点,则跳过continuefor neighbor, weight in graph[current_node].items():distance = min_distance + weight  # 计算起始节点到邻居节点的距离if distance < distances[neighbor]:  # 如果找到了更短的路径,则更新距离并加入堆中distances[neighbor] = distanceheapq.heappush(pq, (distance, neighbor))return distances# 测试
graph = {'A': {'B': 5, 'C': 1},'B': {'A': 5, 'C': 2, 'D': 1},'C': {'A': 1, 'B': 2, 'D': 4, 'E': 8},'D': {'B': 1, 'C': 4, 'E': 3, 'F': 6},'E': {'C': 8, 'D': 3},'F': {'D': 6}}
distances = dijkstra(graph, 'A')
print(distances)  # 输出 {'A': 0, 'B': 5, 'C': 1, 'D': 6, 'E': 9, 'F': 12}

在Dijkstra算法中,我们使用一个堆来维护当前待处理的节点,并从中选择距离最小的节点。我们递归地处理每个未访问的邻居节点,在找到从起始节点到目标节点的最短路径后,我们将距离更新为已知最短距离。最终,我们得到从起始节点到各个节点的最短路径和距离。

6. A* 搜索算法(A *Search Algorithm)

A* 搜索算法是一种启发式搜索算法,用于解决路径规划和图搜索问题。A *算法结合了广度优先搜索和深度优先搜索的优点,使用启发式函数(Heuristic Function)来估计从当前顶点到目标顶点的距离,从而减少搜索空间。

A* 算法在游戏编程、人工智能、机器人导航等领域有着广泛的应用。其时间复杂度取决于启发式函数的选择,理想情况下,可以达到O(b *d),其中b是分支因子,d是最优解的深度。

这是一个A*搜索算法的Python演示:

import heapqdef heuristic(node, goal):"""估价函数,返回当前节点到目标节点的曼哈顿距离:param node: 当前节点:param goal: 目标节点:return: 曼哈顿距离"""(x1, y1) = node(x2, y2) = goalreturn abs(x1 - x2) + abs(y1 - y2)def a_star(graph, start, goal):"""A*搜索算法函数:param graph: 迷宫地图:param start: 起始节点:param goal: 目标节点:return: 路径和距离"""frontier = [(heuristic(start, goal), start)]  # 使用堆来存储当前待处理的节点队列visited = set()  # 记录已访问过的节点distance = {start: 0}  # 记录起始节点到当前节点的距离parent = {start: None}  # 记录每个节点的父节点while frontier:(_, current_node) = heapq.heappop(frontier)if current_node == goal:  # 找到目标节点时,回溯路径并计算距离path = []while current_node is not None:path.append(current_node)current_node = parent[current_node]path.reverse()return sum(distance[node] for node in path), pathvisited.add(current_node)for neighbor in graph[current_node]:if neighbor not in visited:distance_to_neighbor = distance[current_node] + 1  # 当前节点到邻居节点的距离为1if neighbor not in distance or distance_to_neighbor < distance[neighbor]:distance[neighbor] = distance_to_neighborpriority = distance_to_neighbor + heuristic(neighbor, goal)  # 优先级等于已走步数+曼哈顿距离heapq.heappush(frontier, (priority, neighbor))parent[neighbor] = current_nodereturn None# 测试
graph = {(0, 0): [(0, 1), (1, 0)],(0, 1): [(0, 0), (0, 2), (1, 1)],(0, 2): [(0, 1), (1, 2)],(1, 0): [(0, 0), (1, 1), (2, 0)],(1, 1): [(0, 1), (1, 0), (1, 2), (2, 1)],(1, 2): [(0, 2), (1, 1), (2, 2)],(2, 0): [(1, 0), (2, 1)],(2, 1): [(1, 1), (2, 0), (2, 2)],(2, 2): [(1, 2), (2, 1)]}
start = (0, 0)
goal = (2, 2)
distance, path = a_star(graph, start, goal)
print(distance)  # 输出 4
print(path)  # 输出 [(0, 0), (1, 0), (1, 1), (1, 2), (2, 2)]

在A*搜索算法中,我们使用一个堆来维护当前待处理的节点,并从中选择优先级最高的节点。我们使用估价函数来估计从当前节点到目标节点的距离,并通过已知的最短路径长度和这个估计值来确定每个节点的优先级。我们递归地处理每个邻居节点,在找到从起始节点到目标节点的最短路径后,回溯路径并计算距离。最终,我们得到从起始节点到目标节点的最短路径和距离。

7. K-Means聚类算法

K-Means是一种无监督学习算法,用于将数据集划分为K个聚类。K-Means算法通过迭代更新质心(Centroid)来最小化每个聚类内部的平方误差和。其主要步骤如下:

  1. 随机选择K个数据点作为初始质心。
  2. 将每个数据点分配到最近的质心所在的聚类。
  3. 更新质心为每个聚类的均值。
  4. 重复步骤2和3,直到质心不再发生变化。

K-Means算法在数据挖掘、图像处理、市场分析等领域有着广泛的应用。其时间复杂度为O(n * I * K),其中n是数据点的数量,I是迭代次数,K是聚类数目。

这是一个K-Means聚类算法的Python演示:

import randomdef euclidean_distance(x, y):"""计算两个向量之间的欧几里得距离:param x: 向量x:param y: 向量y:return: 欧几里得距离"""return sum((xi - yi) ** 2 for xi, yi in zip(x, y)) ** 0.5def kmeans(data, k):"""K-Means聚类算法函数:param data: 数据集:param k: 聚类个数:return: 聚类结果和每个簇的中心点坐标"""# 随机初始化中心点centers = random.sample(data, k)while True:# 初始化k个空簇clusters = [[] for _ in range(k)]for point in data:# 对于每个数据点,计算其到每个中心点的欧几里得距离,并将其归到距离最近的簇中distances = [euclidean_distance(point, center) for center in centers]nearest_center_index = distances.index(min(distances))clusters[nearest_center_index].append(point)new_centers = []for cluster in clusters:if not cluster:# 如果某个簇为空,则随机选择一个数据点作为新的中心点new_centers.append(random.choice(data))else:# 计算该簇中所有数据点的平均值,并将其作为新的中心点new_centers.append([sum(feature) / len(cluster) for feature in zip(*cluster)])# 如果新中心点与旧中心点相同,则结束迭代if new_centers == centers:breakelse:centers = new_centersreturn clusters, centers# 测试
data = [[1, 2], [3, 4], [7, 8], [9, 10], [11, 12], [13, 14]]
k = 2
clusters, centers = kmeans(data, k)
print(clusters)  # 输出 [[(1, 2), (3, 4)], [(7, 8), (9, 10), (11, 12), (13, 14)]]
print(centers)  # 输出 [[2.0, 3.0], [10.0, 11.0]]

在K-Means聚类算法中,我们首先随机初始化k个中心点。然后对于每个数据点,计算其到所有中心点的欧几里得距离,并将其归到距离最近的簇中。接着,计算每个簇中所有数据点的平均值,并将其作为该簇的新中心点。重复以上步骤,直到新中心点与旧中心点相同,算法结束。最终,我们得到了k个簇以及每个簇的中心点坐标。

以上仅列举了部分令人惊艳的高效算法,计算机科学领域还有许多其他值得关注的算法。这些算法为我们解决实际问题提供了强大的工具,让我们对计算机科学的发展充满信心。希望这篇博客能激发你对算法的兴趣,鼓励你深入学习,并在实践中应用这些算法。


http://www.ppmy.cn/news/241335.html

相关文章

matlab作业 阳光的快乐老爹,霍思燕6岁儿子近照曝光,调皮起来超阳光,完美继承老爹容颜!...

霍思燕6岁儿子近照曝光&#xff0c;调皮起来超阳光&#xff0c;完美继承老爹容颜&#xff01; 明星带娃一起参加真人秀是最近几年比较常见的节目形式&#xff0c;也让我们近距离的关注到一批可爱呆萌的星二代。因为父母都是演艺圈的&#xff0c;所以大部分星二代长得都是非常好…

53岁“聂小凤”携女现身 女儿被赞最美星二代

53岁“聂小凤”携女现身 女儿被赞最美星二代 &#xff08;中娱立赢报道&#xff09;林家有女初长成&#xff01;昔日无线当家花旦龚慈恩带着16岁爱女林恺铃&#xff08;Ashley&#xff09;现身参加活动。虽然Ashley曝光不是第一次&#xff0c;但她每次出现都依然吸引大众的目光…

唤醒队列java_Java队列学习第一篇之列介绍

Java并发之显式锁和隐式锁的区别 在面试的过程中有可能会问到&#xff1a;在Java并发编程中&#xff0c;锁有两种实现&#xff1a;使用隐式锁和使用显示锁分别是什么&#xff1f;两者的区别是什么&#xff1f;所谓的显式锁和隐式锁的区别也就是说说Synchronized(下文简称&#…

java 显式锁_Java并发之显式锁和隐式锁的区别

Java并发之显式锁和隐式锁的区别 在面试的过程中有可能会问到&#xff1a;在Java并发编程中&#xff0c;锁有两种实现&#xff1a;使用隐式锁和使用显示锁分别是什么&#xff1f;两者的区别是什么&#xff1f;所谓的显式锁和隐式锁的区别也就是说说Synchronized(下文简称&#…

swift野梦抄袭 taylor_霉霉Taylor Swift今日出新单,歌词甜腻得让我联想到多年前的那位“野梦男主”!...

这些照片 简直让小编在电脑前脸红心跳到不行&#xff0c; 有人问&#xff0c;眼前这位艳福不错的帅小伙是谁&#xff1f; 被霉霉钦点为《Wildest Dreams》MV男主角的他来头可不小哦&#xff0c; 他可是好莱坞著名演员和导演克林特伊斯特伍德(人称东木老爷子)的儿子 ——史考特伊…

c罗讲什么语言教学,你知道C罗、梅西怎么教育孩子吗?

原标题&#xff1a;你知道C罗、梅西怎么教育孩子吗&#xff1f; 世界杯已经拉开帷幕&#xff0c;时间如白马过隙&#xff0c;昔日球场上那些陪伴我们度过青春岁月的巨星的娃都已经慢慢成长起来&#xff0c;掐指一算&#xff0c;十多年后的世界杯&#xff0c;极有可能被这群附有…

php额拍戏,像这种会演戏的演员,给我焊在剧组365天拍戏可以吗?

最近芭姐疯狂 get 到董子健的演技&#xff0c;每晚换台一边《大江大河 2》一边《流金岁月》交叉着看&#xff0c;太直观了&#xff01; 《大江大河》中&#xff0c;董子健饰演的杨巡虽然戏份不及宋运辉多&#xff0c;但在有限的笔墨中&#xff0c;董子健凭借到位的演技&#xf…

金融计算机怎么学,怎么选,计算机还是金融专业?学姐:看以下4点

经常有朋友问&#xff1a;读金融好还是读计算机好&#xff1f; 金融和计算机是现在最热门的专业&#xff0c;收入高&#xff0c;金融人士和码农简直是金矿的代名词。问这两个专业的好坏&#xff0c;有点像是在问&#xff1a;清华和北大哪个好&#xff1f; 每次我总是回答&#…