文章目录
- 基础排序算法
-
- [1. 冒泡排序 (Bubble Sort)](#1. 冒泡排序 (Bubble Sort))
- [2. 快速排序 (Quick Sort)](#2. 快速排序 (Quick Sort))
- [3. 插入排序 (Insertion Sort)](#3. 插入排序 (Insertion Sort))
- [4. 选择排序 (Selection Sort)](#4. 选择排序 (Selection Sort))
- 进阶搜索与查找
-
- [5. 归并排序 (Merge Sort)](#5. 归并排序 (Merge Sort))
- [6. 堆排序 (Heap Sort)](#6. 堆排序 (Heap Sort))
- [7. 深度优先搜索 (DFS)](#7. 深度优先搜索 (DFS))
- [8. 二分查找 (Binary Search)](#8. 二分查找 (Binary Search))
- 算法设计思想
-
- [9. 动态规划 (DP)](#9. 动态规划 (DP))
- [10. 贪心算法 (Greedy)](#10. 贪心算法 (Greedy))
- [11. 回溯算法 (Backtracking)](#11. 回溯算法 (Backtracking))
- [12. 递归算法 (Recursion)](#12. 递归算法 (Recursion))
- 复杂结构与数学算法
-
- [13. 哈希算法 (Hashing)](#13. 哈希算法 (Hashing))
- [14. 贝叶斯算法 (Naive Bayes)](#14. 贝叶斯算法 (Naive Bayes))
- [15. 分治算法 (Divide and Conquer)](#15. 分治算法 (Divide and Conquer))
- [16. K-Means 聚类](#16. K-Means 聚类)
- [17. 迪杰斯特拉算法 (Dijkstra)](#17. 迪杰斯特拉算法 (Dijkstra))
- [18. 红黑树 (Red-Black Tree)](#18. 红黑树 (Red-Black Tree))
- [19. 布隆过滤器 (Bloom Filter)](#19. 布隆过滤器 (Bloom Filter))
基础排序算法
1. 冒泡排序 (Bubble Sort)
本质: 通过相邻元素的"两两比较"和"位置交换",每轮把最大的放到末尾。
csharp
public static void BubbleSort(int[] arr)
{
for (int i = 0; i < arr.Length - 1; i++)
{
for (int j = 0; j < arr.Length - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
(arr[j], arr[j + 1]) = (arr[j + 1], arr[j]);
}
}
}
}
2. 快速排序 (Quick Sort)
本质: 选一个基准点 (Pivot),小的放左边,大的放右边,递归执行。
csharp
public static void QuickSort(int[] arr, int left, int right)
{
if (left >= right) return;
int pivot = arr[left];
int i = left, j = right;
while (i < j)
{
while (i < j && arr[j] >= pivot) j--;
arr[i] = arr[j];
while (i < j && arr[i] <= pivot) i++;
arr[j] = arr[i];
}
arr[i] = pivot;
QuickSort(arr, left, i - 1);
QuickSort(arr, i + 1, right);
}
3. 插入排序 (Insertion Sort)
本质: 将未排序序列中的元素,在已排序序列中从后向前扫描,找到相应位置并插入。
csharp
public static void InsertionSort(int[] arr)
{
for (int i = 1; i < arr.Length; i++)
{
int current = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > current)
{
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = current;
}
}
4. 选择排序 (Selection Sort)
本质: 每一轮都从剩余未排序元素中"选中"最小的一个,放到已排序序列的末尾。
csharp
public static void SelectionSort(int[] arr)
{
for (int i = 0; i < arr.Length - 1; i++)
{
int minIndex = i;
for (int j = i + 1; j < arr.Length; j++)
{
if (arr[j] < arr[minIndex])
minIndex = j;
}
(arr[i], arr[minIndex]) = (arr[minIndex], arr[i]);
}
}
进阶搜索与查找
5. 归并排序 (Merge Sort)
本质: 将数组拆成最小单元,再两两有序合并。
csharp
public static void MergeSort(int[] arr, int left, int right)
{
if (left >= right) return;
int mid = (left + right) / 2;
MergeSort(arr, left, mid);
MergeSort(arr, mid + 1, right);
Merge(arr, left, mid, right);
}
private static void Merge(int[] arr, int left, int mid, int right)
{
int[] temp = new int[right - left + 1];
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right)
{
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
for (int p = 0; p < temp.Length; p++)
arr[left + p] = temp[p];
}
6. 堆排序 (Heap Sort)
本质: 利用大顶堆的性质,把数组构造成堆,再依次取出堆顶元素。
csharp
public static void HeapSort(int[] arr)
{
for (int i = arr.Length / 2 - 1; i >= 0; i--)
Heapify(arr, arr.Length, i);
for (int i = arr.Length - 1; i > 0; i--)
{
(arr[0], arr[i]) = (arr[i], arr[0]);
Heapify(arr, i, 0);
}
}
private static void Heapify(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)
{
(arr[i], arr[largest]) = (arr[largest], arr[i]);
Heapify(arr, n, largest);
}
}
7. 深度优先搜索 (DFS)
本质: 从起点出发,尽可能深地访问每个分支,直到无法前进再回溯
csharp
public class DFSExample
{
private static List<int>[] adj;
private static bool[] visited;
public static void DFS(int node)
{
Console.Write(node + " ");
visited[node] = true;
foreach (int neighbor in adj[node])
{
if (!visited[neighbor])
DFS(neighbor);
}
}
public static void Main()
{
int n = 5;
adj = new List<int>[n];
visited = new bool[n];
for (int i = 0; i < n; i++) adj[i] = new List<int>();
adj[0].Add(1); adj[0].Add(2);
adj[1].Add(3); adj[2].Add(4);
DFS(0); // 输出: 0 1 3 2 4
}
}
8. 二分查找 (Binary Search)
本质: 在有序数组中,每次取中间元素和目标值比较,缩小一半范围。前提是数组必须有序。
csharp
public static int BinarySearch(int[] arr, int target)
{
int left = 0, right = arr.Length - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
算法设计思想
9. 动态规划 (DP)
本质: 拆分重叠子问题,用表格记录中间结果(避免重复计算)。
把大问题拆成重叠的子问题,记录子问题的解避免重复计算(以斐波那契为例)
csharp
// 典型:01背包或爬楼梯
public int ClimbStairs(int n)
{
if (n <= 2) return n;
int a = 1, b = 2;
for (int i = 3; i <= n; i++)
(a, b) = (b, a + b);
return b;
}
// 斐波那契
public static int FibonacciDP(int n)
{
if (n <= 1) return n;
int[] dp = new int[n + 1];
dp[0] = 0; dp[1] = 1;
for (int i = 2; i <= n; i++)
dp[i] = dp[i - 1] + dp[i - 2];
return dp[n];
}
10. 贪心算法 (Greedy)
本质: 每一步都拿眼前的最优解,不考虑全局,每一步都做出当前最优的选择,希望得到全局最优解(以硬币找零为例)。
csharp
public static int CoinChangeGreedy(int[] coins, int amount)
{
Array.Sort(coins);
int count = 0;
for (int i = coins.Length - 1; i >= 0; i--)
{
while (amount >= coins[i])
{
amount -= coins[i];
count++;
}
}
return amount == 0 ? count : -1;
}
11. 回溯算法 (Backtracking)
本质: 尝试所有可能的解,遇到错误就回溯(以全排列为例)。
csharp
public static IList<IList<int>> Permute(int[] nums)
{
IList<IList<int>> result = new List<IList<int>>();
Backtrack(nums, new List<int>(), new bool[nums.Length], result);
return result;
}
private static void Backtrack(int[] nums, List<int> path, bool[] used, IList<IList<int>> result)
{
if (path.Count == nums.Length)
{
result.Add(new List<int>(path));
return;
}
for (int i = 0; i < nums.Length; i++)
{
if (used[i]) continue;
used[i] = true;
path.Add(nums[i]);
Backtrack(nums, path, used, result);
path.RemoveAt(path.Count - 1);
used[i] = false;
}
}
12. 递归算法 (Recursion)
**本质:**函数直接或间接调用自身(以阶乘为例)
csharp
public static int Factorial(int n)
{
if (n == 0) return 1;
return n * Factorial(n - 1);
}
复杂结构与数学算法
13. 哈希算法 (Hashing)
本质: 将任意长度的数据通过映射,生成固定长度的摘要(键值对存储的基础)。
csharp
public class HashExample
{
public static void Main()
{
Dictionary<string, int> hashMap = new Dictionary<string, int>();
hashMap["Alice"] = 25;
hashMap["Bob"] = 30;
Console.WriteLine(hashMap["Alice"]); // 输出 25
}
}
14. 贝叶斯算法 (Naive Bayes)
本质: 基于贝叶斯定理的概率分类方法。这里给一个简化的文本分类示例。
- 贝叶斯定理 = 用结果反推原因的概率
- 贝叶斯分类 = 算属于每个类别的概率,选最大的
- 最常用场景:文本分类、垃圾邮件、情感判断、新闻分类
它是用来算「逆概率」的公式 ------ 根据已经发生的结果,反推原因的概率。
P(A∣B)=P(B)P(B∣A)×P(A)
在已经看到现象 B 发生 的情况下,原因是 A 的概率 =
- 【A 发生时,出现 B 的概率】
- × 【A 本身发生的概率】
- ÷ 【B 出现的总概率】
最经典的例子:垃圾邮件分类
我们要判断一封邮件:是 垃圾邮件 还是 正常邮件?
已知条件
- 所有邮件里,50% 是垃圾邮件,50% 是正常邮件
- 垃圾邮件里,80% 包含 "免费" 这个词
- 正常邮件里,10% 包含 "免费" 这个词
问题
现在收到一封邮件,里面有 "免费",请问它是垃圾邮件的概率是多少?
- A = 垃圾邮件
- B = 邮件里有 "免费"
求:P (A|B) = 有免费 → 是垃圾邮件的概率
- P (B|A) = 垃圾邮件里有免费 = 80% = 0.8
- P (A) = 垃圾邮件本身概率 = 0.5
- P (B) = 所有邮件里出现免费的总概率 = 0.5×0.8 + 0.5×0.1 = 0.45
P(A∣B)=(0.8×0.5)÷0.45≈89%
结论:这封邮件有 89% 是垃圾邮件!
csharp
public class NaiveBayes
{
private Dictionary<string, int> spamWordCounts = new Dictionary<string, int>();
private Dictionary<string, int> hamWordCounts = new Dictionary<string, int>();
private int spamCount = 0, hamCount = 0;
public void Train(string[] words, bool isSpam)
{
if (isSpam)
{
spamCount++;
foreach (var word in words)
{
if (spamWordCounts.ContainsKey(word))
spamWordCounts[word]++;
else
spamWordCounts[word] = 1;
}
}
else
{
hamCount++;
foreach (var word in words)
{
if (hamWordCounts.ContainsKey(word))
hamWordCounts[word]++;
else
hamWordCounts[word] = 1;
}
}
}
public bool Predict(string[] words)
{
double spamProb = Math.Log((double)spamCount / (spamCount + hamCount));
double hamProb = Math.Log((double)hamCount / (spamCount + hamCount));
foreach (var word in words)
{
spamProb += Math.Log((spamWordCounts.ContainsKey(word) ? spamWordCounts[word] : 0) + 1) / (spamCount + 2);
hamProb += Math.Log((hamWordCounts.ContainsKey(word) ? hamWordCounts[word] : 0) + 1) / (hamCount + 2);
}
return spamProb > hamProb;
}
}
15. 分治算法 (Divide and Conquer)
本质: 分而治之,如归并、快排、二分,把问题分成多个子问题,分别解决后合并结果(和归并排序类似,这里以最大子数组和为例)。
csharp
public static int MaxSubArray(int[] arr, int left, int right)
{
if (left == right) return arr[left];
int mid = (left + right) / 2;
int leftMax = MaxSubArray(arr, left, mid);
int rightMax = MaxSubArray(arr, mid + 1, right);
int crossMax = MaxCrossingSum(arr, left, mid, right);
return Math.Max(Math.Max(leftMax, rightMax), crossMax);
}
private static int MaxCrossingSum(int[] arr, int left, int mid, int right)
{
int sum = 0, leftSum = int.MinValue;
for (int i = mid; i >= left; i--)
{
sum += arr[i];
if (sum > leftSum) leftSum = sum;
}
sum = 0;
int rightSum = int.MinValue;
for (int i = mid + 1; i <= right; i++)
{
sum += arr[i];
if (sum > rightSum) rightSum = sum;
}
return leftSum + rightSum;
}
16. K-Means 聚类
本质: 把数据分成 K 个簇,迭代更新簇中心直到收敛。
csharp
public class KMeans
{
public static List<List<double[]>> Cluster(double[][] data, int k, int maxIterations)
{
Random rand = new Random();
List<double[]> centroids = new List<double[]>();
for (int i = 0; i < k; i++)
centroids.Add(data[rand.Next(data.Length)]);
for (int iter = 0; iter < maxIterations; iter++)
{
List<List<double[]>> clusters = new List<List<double[]>>();
for (int i = 0; i < k; i++) clusters.Add(new List<double[]>());
foreach (var point in data)
{
int clusterIndex = 0;
double minDist = double.MaxValue;
for (int i = 0; i < k; i++)
{
double dist = Distance(point, centroids[i]);
if (dist < minDist)
{
minDist = dist;
clusterIndex = i;
}
}
clusters[clusterIndex].Add(point);
}
List<double[]> newCentroids = new List<double[]>();
foreach (var cluster in clusters)
{
if (cluster.Count == 0) continue;
double[] newCentroid = new double[cluster[0].Length];
for (int j = 0; j < cluster[0].Length; j++)
{
double sum = 0;
foreach (var p in cluster) sum += p[j];
newCentroid[j] = sum / cluster.Count;
}
newCentroids.Add(newCentroid);
}
if (centroids.SequenceEqual(newCentroids)) break;
centroids = newCentroids;
}
return null;
}
private static double Distance(double[] a, double[] b)
{
double sum = 0;
for (int i = 0; i < a.Length; i++)
sum += Math.Pow(a[i] - b[i], 2);
return Math.Sqrt(sum);
}
}
17. 迪杰斯特拉算法 (Dijkstra)
本质: 图论中求单源最短路径,求单源最短路径,每次选距离起点最近的节点更新邻居距离。
csharp
public static int[] Dijkstra(int[,] graph, int start)
{
int n = graph.GetLength(0);
int[] dist = new int[n];
bool[] visited = new bool[n];
Array.Fill(dist, int.MaxValue);
dist[start] = 0;
for (int i = 0; i < n; i++)
{
int u = -1;
for (int j = 0; j < n; j++)
{
if (!visited[j] && (u == -1 || dist[j] < dist[u]))
u = j;
}
visited[u] = true;
for (int v = 0; v < n; v++)
{
if (!visited[v] && graph[u, v] != 0 && dist[u] + graph[u, v] < dist[v])
dist[v] = dist[u] + graph[u, v];
}
}
return dist;
}
18. 红黑树 (Red-Black Tree)
本质: 一种自平衡二叉查找树,通过颜色规则保持树的平衡。C# 中 SortedSet 底层就是类似结构,这里给一个简化的节点定义。
csharp
public enum NodeColor { Red, Black }
public class RedBlackNode<T> where T : IComparable<T>
{
public T Value { get; set; }
public NodeColor Color { get; set; }
public RedBlackNode<T> Left { get; set; }
public RedBlackNode<T> Right { get; set; }
public RedBlackNode<T> Parent { get; set; }
public RedBlackNode(T value)
{
Value = value;
Color = NodeColor.Red;
}
}
// C# 标准库 SortedSet<T> 就是基于红黑树实现的,可直接使用:
public class RedBlackExample
{
public static void Main()
{
SortedSet<int> set = new SortedSet<int> { 5, 3, 7, 1, 9 };
foreach (var item in set) Console.Write(item + " "); // 输出 1 3 5 7 9
}
}
19. 布隆过滤器 (Bloom Filter)
本质: :用多个哈希函数把元素映射到位数组中,判断元素是否可能存在。
csharp
public class BloomFilter
{
private readonly BitArray bits;
private readonly int hashCount;
public BloomFilter(int size, int hashCount)
{
bits = new BitArray(size);
this.hashCount = hashCount;
}
public void Add(string item)
{
for (int i = 0; i < hashCount; i++)
{
int hash = GetHash(item, i) % bits.Length;
bits.Set(hash, true);
}
}
public bool MightContain(string item)
{
for (int i = 0; i < hashCount; i++)
{
int hash = GetHash(item, i) % bits.Length;
if (!bits.Get(hash)) return false;
}
return true;
}
private int GetHash(string item, int seed)
{
unchecked
{
int hash = seed;
foreach (char c in item)
hash = hash * 31 + c;
return hash & int.MaxValue;
}
}
}