# 常用算法简介

URL: https://caijiao.org/cpp/stl/algorithms
Source: docs/cpp/stl/algorithms.md
Description: 本节介绍常用的算法，涵盖排序、查找等基本算法，帮助理解其原理和应用场景。

算法是解决问题的步骤和方法，它们在计算机科学中起着至关重要的作用。在 C++ 编程中，熟悉常用的算法不仅能提升编程效率，还能帮助我们更好地优化程序性能。下面将简要介绍几种常见的算法，包括排序、查找等基础算法。

## 排序算法

排序是将一组数据按照一定的规则（通常是升序或降序）进行排列的过程。常见的排序算法包括：

### 1. 冒泡排序

冒泡排序是一种简单的排序算法，重复地走访待排序的元素，每次比较相邻元素，如果顺序错误就交换它们。每次遍历后，最大的元素会被“冒泡”到数组的末尾。

```cpp
void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        for (int j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                std::swap(arr[j], arr[j+1]);
            }
        }
    }
}
````

* 时间复杂度：O(n^2)
* 优点：实现简单
* 缺点：效率低，不适合大规模数据

### 2. 快速排序

快速排序是基于分治法的排序算法，选择一个“基准”元素，将数据分为两部分，左边比基准小，右边比基准大，然后递归排序。

```cpp
int partition(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(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);
    }
}
```

* 时间复杂度：O(n log n)（平均情况）
* 优点：效率高，特别是对于大规模数据
* 缺点：最坏情况下为O(n^2)

## 查找算法

查找是从一组数据中找到某个元素的位置或判断其是否存在的过程。常见的查找算法包括：

### 1. 顺序查找

顺序查找是一种简单的查找方法，从头到尾依次比较元素，直到找到目标元素或遍历完所有元素。

```cpp
int sequentialSearch(int arr[], int n, int target) {
    for (int i = 0; i < n; i++) {
        if (arr[i] == target) {
            return i;
        }
    }
    return -1;  // 未找到
}
```

* 时间复杂度：O(n)
* 优点：简单直观
* 缺点：效率低，尤其在数据量大的时候

### 2. 二分查找

二分查找是一种高效的查找算法，要求数据是已排序的。每次通过比较中间元素与目标元素的大小，来缩小查找范围。

```cpp
int binarySearch(int arr[], int low, int high, int target) {
    while (low <= high) {
        int mid = low + (high - low) / 2;
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    return -1;  // 未找到
}
```

* 时间复杂度：O(log n)
* 优点：高效，特别是对于大规模已排序数据
* 缺点：只能用于已排序的数据

## 图算法

图算法用于解决图论中的问题，常见的图算法包括：

### 1. 深度优先搜索（DFS）

深度优先搜索是一种遍历或搜索图的算法，从起始节点开始，沿着图的边一直走到底，直到没有未访问的节点为止。

```cpp
void DFS(int v, vector<bool>& visited, const vector<vector<int>>& adj) {
    visited[v] = true;
    cout << v << " ";

    for (int u : adj[v]) {
        if (!visited[u]) {
            DFS(u, visited, adj);
        }
    }
}
```

* 时间复杂度：O(V + E)，其中V为顶点数，E为边数
* 优点：适用于寻找图中所有的连通分量
* 缺点：对于某些图结构，可能会消耗大量内存

### 2. 广度优先搜索（BFS）

广度优先搜索是一种逐层遍历图的算法，从起始节点开始，先访问与起始节点距离为1的节点，再访问距离为2的节点，以此类推。

```cpp
void BFS(int s, vector<bool>& visited, const vector<vector<int>>& adj) {
    queue<int> q;
    visited[s] = true;
    q.push(s);

    while (!q.empty()) {
        int v = q.front();
        q.pop();
        cout << v << " ";

        for (int u : adj[v]) {
            if (!visited[u]) {
                visited[u] = true;
                q.push(u);
            }
        }
    }
}
```

* 时间复杂度：O(V + E)
* 优点：适合寻找最短路径
* 缺点：可能需要较多的内存

## 动态规划

动态规划（DP）是一种用于解决最优化问题的算法，通过将问题分解为更小的子问题并保存其结果来避免重复计算。常见的动态规划问题有背包问题、最长公共子序列等。

### 1. 背包问题

背包问题要求在给定的物品集合和背包容量下，找到能够放入背包的物品的最大价值。使用动态规划解决这个问题时，通过构建一个二维数组 `dp[i][w]`，表示在前 `i` 个物品中选择一些物品放入容量为 `w` 的背包中能够获得的最大价值。

```cpp
int knapsack(int W, vector<int>& wt, vector<int>& val, int n) {
    vector<vector<int>> dp(n + 1, 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] = max(dp[i - 1][w], dp[i - 1][w - wt[i - 1]] + val[i - 1]);
            } else {
                dp[i][w] = dp[i - 1][w];
            }
        }
    }

    return dp[n][W];
}
```

* 时间复杂度：O(nW)，其中 `n` 为物品个数，`W` 为背包容量
* 优点：适用于最优化问题
* 缺点：空间复杂度较高，特别是对于大规模问题

---

常用算法有助于提高程序的执行效率和优化解决方案。掌握这些基础算法，并根据具体应用场景进行选择，可以大大提高代码的性能和可维护性。
