C++作为一门功能强大且广泛应用的编程语言,其在算法领域的应用十分广泛。掌握一些经典的算法不仅能够提高程序的效率,还能增强解决复杂问题的能力。本文将介绍一些在C++中广泛应用的经典算法,包括排序算法、搜索算法、图算法、动态规划等。
1. 排序算法
1.1 快速排序(Quick Sort)
快速排序是一种基于分治思想的排序算法,其基本思想是通过一个基准元素将数组分为两部分,使得一部分元素小于基准元素,另一部分元素大于基准元素,然后递归地对两部分分别进行快速排序。其平均时间复杂度为O(n log n)。
#include <iostream>
#include <vector>
int partition(std::vector<int>& arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; ++j) {
if (arr[j] < pivot) {
++i;
std::swap(arr[i], arr[j]);
}
}
std::swap(arr[i + 1], arr[high]);
return i + 1;
}
void quickSort(std::vector<int>& arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
int main() {
std::vector<int> arr = {10, 7, 8, 9, 1, 5};
quickSort(arr, 0, arr.size() - 1);
for (int i : arr) {
std::cout << i << " ";
}
return 0;
}
1.2 归并排序(Merge Sort)
归并排序同样是一种分治算法,其基本思想是将数组递归地分成两半,分别进行排序,然后再将两个有序的数组合并成一个有序数组。归并排序的时间复杂度为O(n log n),但其空间复杂度较高。
#include <iostream>
#include <vector>
void merge(std::vector<int>& arr, int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
std::vector<int> L(n1), R(n2);
for (int i = 0; i < n1; ++i)
L[i] = arr[l + i];
for (int j = 0; j < n2; ++j)
R[j] = arr[m + 1 + j];
int i = 0, j = 0, k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
++i;
} else {
arr[k] = R[j];
++j;
}
++k;
}
while (i < n1) {
arr[k] = L[i];
++i;
++k;
}
while (j < n2) {
arr[k] = R[j];
++j;
++k;
}
}
void mergeSort(std::vector<int>& arr, int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
int main() {
std::vector<int> arr = {12, 11, 13, 5, 6, 7};
mergeSort(arr, 0, arr.size() - 1);
for (int i : arr) {
std::cout << i << " ";
}
return 0;
}
1.3 堆排序(Heap Sort)
堆排序是一种基于二叉堆数据结构的排序算法。它将待排序的序列构造成一个大顶堆(或小顶堆),然后依次取出堆顶元素并调整堆以保证其有序。堆排序的时间复杂度为O(n log n),且不需要额外的内存空间。
#include <iostream>
#include <vector>
void heapify(std::vector<int>& arr, int n, int i) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < n && arr[left] > arr[largest])
largest = left;
if (right < n && arr[right] > arr[largest])
largest = right;
if (largest != i) {
std::swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
void heapSort(std::vector<int>& arr) {
int n = arr.size();
for (int i = n / 2 - 1; i >= 0; --i)
heapify(arr, n, i);
for (int i = n - 1; i > 0; --i) {
std::swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
int main() {
std::vector<int> arr = {4, 10, 3, 5, 1};
heapSort(arr);
for (int i : arr) {
std::cout << i << " ";
}
return 0;
}
2. 搜索算法
2.1 二分查找(Binary Search)
二分查找是一种高效的搜索算法,适用于在有序数组中查找特定元素。其基本思想是将数组从中间一分为二,与目标值比较后选择在左半部分或右半部分继续查找,直到找到目标值或范围缩小为空。二分查找的时间复杂度为O(log n)。
#include <iostream>
#include <vector>
int binarySearch(const std::vector<int>& arr, int l, int r, int x) {
while (l <= r) {
int m = l + (r - l) / 2;
if (arr[m] == x)
return m;
if (arr[m] < x)
l = m + 1;
else
r = m - 1;
}
return -1;
}
int main() {
std::vector<int> arr = {2, 3, 4, 10, 40};
int x = 10;
int result = binarySearch(arr, 0, arr.size() - 1, x);
if (result == -1)
std::cout << "Element not present in array";
else
std::cout << "Element found at index " << result;
return 0;
}
2.2 深度优先搜索(Depth-First Search, DFS)
深度优先搜索是一种图遍历算法,其基本思想是沿着一个分支不断向下搜索,直到无法继续为止,然后回溯并从其他分支继续搜索。DFS常用于解决连通性问题、生成迷宫、寻找路径等。时间复杂度为O(V + E),其中V是顶点数,E是边数。
#include <iostream>
#include <vector>
void DFSUtil(int v, std::vector<bool>& visited, const std::vector<std::vector<int>>& adj) {
visited[v] = true;
std::cout << v << " ";
for (int i : adj[v])
if (!visited[i])
DFSUtil(i, visited, adj);
}
void DFS(int v, const std::vector<std::vector<int>>& adj) {
std::vector<bool> visited(adj.size(), false);
DFSUtil(v, visited, adj);
}
int main() {
std::vector<std::vector<int>> adj = {
{1, 2},
{0, 3, 4},
{0, 5},
{1},
{1},
{2}
};
DFS(0, adj);
return 0;
}
2.3 广度优先搜索(Breadth-First Search, BFS)
广度优先搜索是另一种图遍历算法,其基本思想是从一个起始顶点出发,依次访问其所有邻接顶点,然后再从这些顶点出发继续访问其未被访问的邻接顶点,直到所有顶点都被访问。BFS常用于求解最短路径问题。时间复杂度为O(V + E)。
#include <iostream>
#include <vector>
#include <queue>
void BFS(int v, const std::vector<std::vector<int>>& adj) {
std::vector<bool> visited(adj.size(), false);
std::queue<int> q;
visited[v] = true;
q.push(v);
while (!q.empty()) {
int u = q.front();
q.pop();
std::cout << u << " ";
for (int i : adj[u]) {
if (!visited[i]) {
visited[i] = true;
q.push(i);
}
}
}
}
int main() {
std::vector<std::vector<int>> adj = {
{1, 2},
{0, 3, 4},
{0, 5},
{1},
{1},
{2}
};
BFS(0, adj);
return 0;
}
3. 动态规划(Dynamic Programming)
3.1 最长公共子序列(Longest Common Subsequence, LCS)
最长公共子序列问题是经典的动态规划问题,其目标是在两个序列中找到最长的公共子序列。通过构建一个二维DP表格,逐步填充每个子问题的最优解,从而得到整个问题的解。时间复杂度为O(n * m),其中n和m分别为两个序列的长度。
#include <iostream>
#include <vector>
#include <string>
int LCS(const std::string& X, const std::string& Y) {
int n = X.size();
int m = Y.size();
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(m + 1, 0));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (X[i - 1] == Y[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = std::max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[n][m];
}
int main() {
std::string X = "AGGTAB";
std::string Y = "GXTXAYB";
std::cout << "Length of LCS is " << LCS(X, Y);
return 0;
}
3.2 背包问题(Knapsack Problem)
背包问题是另一个经典的动态规划问题,其目标是在给定物品的重量和价值的情况下,选择物品装入背包使得背包中的物品总价值最大化,而不超过背包的容量。使用动态规划可以有效解决这一问题,时间复杂度为O(n * W),其中n为物品数,W为背包容量。
#include <iostream>
#include <vector>
int knapSack(int W, const std::vector<int>& wt, const std::vector<int>& val, int n) {
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(W + 1, 0));
for (int i = 1; i <= n; ++i) {
for (int w = 1; w <= W; ++w) {
if (wt[i - 1] <= w)
dp[i][w] = std::max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i - 1][w]);
else
dp[i][w] = dp[i - 1][w];
}
}
return dp[n][W];
}
int main() {
std::vector<int> val = {60, 100, 120};
std::vector<int> wt = {10, 20, 30};
int W = 50;
int n = val.size();
std::cout << "Maximum value in Knapsack = " << knapSack(W, wt, val, n);
return 0;
}
4. 图算法
4.1 Dijkstra算法
Dijkstra算法用于求解单源最短路径问题,特别是在图中不存在负权边的情况下。其基本思想是从起点开始,逐步找到到其他顶点的最短路径,直到所有顶点都被访问。时间复杂度为O(V^2)或O((V + E) log V)(使用优先队列时)。
#include <iostream>
#include <vector>
#include <queue>
const int INF = 1e9;
void dijkstra(int src, const std::vector<std::vector<std::pair<int, int>>>& adj) {
int n = adj.size();
std::vector<int> dist(n, INF);
dist[src] = 0;
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<std::pair<int, int>>> pq;
pq.push({0, src});
while (!pq.empty()) {
int u = pq.top().second;
pq.pop();
for (const auto& p : adj[u]) {
int v = p.first;
int weight = p.second;
if (dist[v] > dist[u] + weight) {
dist[v] = dist[u] + weight;
pq.push({dist[v], v});
}
}
}
for (int i = 0; i < n; ++i) {
std::cout << "Distance from " << src << " to " << i << " is " << dist[i] << "\n";
}
}
int main() {
std::vector<std::vector<std::pair<int, int>>> adj = {
{{1, 4}, {2, 1}},
{{3, 1}},
{{1, 2}, {3, 5}},
{}
};
dijkstra(0, adj);
return 0;
}
4.2 Floyd-Warshall算法
Floyd-Warshall算法用于求解图中任意两点间的最短路径问题。其基本思想是通过动态规划逐步更新路径长度,直到所有顶点对之间的最短路径长度都被计算出来。时间复杂度为O(V^3)。
#include <iostream>
#include <vector>
const int INF = 1e9;
void floydWarshall(std::vector<std::vector<int>>& dist) {
int n = dist.size();
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (dist[i][k] < INF && dist[k][j] < INF)
dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
int main() {
std::vector<std::vector<int>> dist = {
{0, 3, INF, INF},
{2, 0, INF, INF},
{INF, 7, 0, 1},
{6, INF, INF, 0}
};
floydWarshall(dist);
for (const auto& row : dist) {
for (int d : row) {
if (d == INF)
std::cout << "INF ";
else
std::cout << d << " ";
}
std::cout << "\n";
}
return 0;
}