• 【蓝桥杯】C语言常见高级算法


    🌸个人主页:Yang-ai-cao
    📕系列专栏:蓝桥杯    C语言
       🍍博学而日参省乎己,知明而行无过矣

    目录

    🌸个人主页:Yang-ai-cao

    📕系列专栏:蓝桥杯    C语言

    🍍博学而日参省乎己,知明而行无过矣

    一、动态规划(Dynamic Programming)

    例子:斐波那契数列

    动态规划解法

    例子:0-1 背包问题

    动态规划解法

    二、贪心算法(Greedy Algorithm)

    例子:活动选择问题

    贪心算法解法

    三、回溯算法(Backtracking)

    例子:N皇后问题

    回溯算法解法

    四、分治算法(Divide and Conquer)

    例子:归并排序(Merge Sort)

    五、图算法(Graph Algorithms)

    例子:Dijkstra 算法(单源最短路径)

    贪心算法(Greedy Algorithm)

    例子:霍夫曼编码(Huffman Coding)

    总结


    一、动态规划(Dynamic Programming)

    例子:斐波那契数列

    动态规划解法
    1. #include
    2. // 计算斐波那契数列的第 n 项
    3. int fib(int n) {
    4. // 创建一个数组来存储斐波那契数列
    5. int f[n+1];
    6. f[0] = 0; // 第 0 项是 0
    7. f[1] = 1; // 第 1 项是 1
    8. // 通过迭代计算第 n 项
    9. for (int i = 2; i <= n; i++) {
    10. f[i] = f[i-1] + f[i-2]; // 当前项等于前两项之和
    11. }
    12. return f[n]; // 返回第 n 项
    13. }
    14. int main() {
    15. int n = 10; // 计算第 10 项
    16. printf("Fibonacci number is %d\n", fib(n)); // 输出结果
    17. return 0;
    18. }

    例子:0-1 背包问题

    动态规划解法
    1. #include
    2. // 返回两个整数中的最大值
    3. int max(int a, int b) { return (a > b) ? a : b; }
    4. // 解决 0-1 背包问题
    5. int knapSack(int W, int wt[], int val[], int n) {
    6. // 创建一个二维数组 K,用于存储子问题的解
    7. int K[n+1][W+1];
    8. // 填充 K 表
    9. for (int i = 0; i <= n; i++) {
    10. for (int w = 0; w <= W; w++) {
    11. if (i == 0 || w == 0) {
    12. K[i][w] = 0; // 基本情况:没有物品或容量为 0
    13. } else if (wt[i-1] <= w) {
    14. // 选择当前物品或不选择当前物品,取最大值
    15. K[i][w] = max(val[i-1] + K[i-1][w-wt[i-1]], K[i-1][w]);
    16. } else {
    17. K[i][w] = K[i-1][w]; // 当前物品不能放入背包
    18. }
    19. }
    20. }
    21. return K[n][W]; // 返回最大价值
    22. }
    23. int main() {
    24. int val[] = {60, 100, 120}; // 物品的价值
    25. int wt[] = {10, 20, 30}; // 物品的重量
    26. int W = 50; // 背包的容量
    27. int n = sizeof(val)/sizeof(val[0]); // 物品的数量
    28. printf("Maximum value in Knapsack = %d\n", knapSack(W, wt, val, n)); // 输出结果
    29. return 0;
    30. }

    二、贪心算法(Greedy Algorithm)

    例子:活动选择问题

    贪心算法解法
    1. #include
    2. // 打印最大数量的可选活动
    3. void printMaxActivities(int s[], int f[], int n) {
    4. int i = 0; // 第一个活动总是被选择
    5. printf("Selected activities: %d ", i);
    6. // 选择其余活动
    7. for (int j = 1; j < n; j++) {
    8. if (s[j] >= f[i]) { // 如果当前活动的开始时间大于等于上一个活动的结束时间
    9. printf("%d ", j);
    10. i = j; // 更新上一个被选择活动的索引
    11. }
    12. }
    13. printf("\n");
    14. }
    15. int main() {
    16. int s[] = {1, 3, 0, 5, 8, 5}; // 活动的开始时间
    17. int f[] = {2, 4, 6, 7, 9, 9}; // 活动的结束时间
    18. int n = sizeof(s)/sizeof(s[0]); // 活动的数量
    19. printMaxActivities(s, f, n); // 输出结果
    20. return 0;
    21. }

    三、回溯算法(Backtracking)

    例子:N皇后问题

    回溯算法解法
    1. #include
    2. #include
    3. #define N 4 // 棋盘大小
    4. // 打印棋盘
    5. void printSolution(int board[N][N]) {
    6. for (int i = 0; i < N; i++) {
    7. for (int j = 0; j < N; j++) {
    8. printf(" %d ", board[i][j]);
    9. }
    10. printf("\n");
    11. }
    12. }
    13. // 检查在 board[row][col] 放置皇后是否安全
    14. bool isSafe(int board[N][N], int row, int col) {
    15. int i, j;
    16. // 检查当前行的左侧
    17. for (i = 0; i < col; i++)
    18. if (board[row][i])
    19. return false;
    20. // 检查左上对角线
    21. for (i = row, j = col; i >= 0 && j >= 0; i--, j--)
    22. if (board[i][j])
    23. return false;
    24. // 检查左下对角线
    25. for (i = row, j = col; j >= 0 && i < N; i++, j--)
    26. if (board[i][j])
    27. return false;
    28. return true;
    29. }
    30. // 递归解决 N 皇后问题
    31. bool solveNQUtil(int board[N][N], int col) {
    32. if (col >= N) // 所有皇后已放置
    33. return true;
    34. for (int i = 0; i < N; i++) {
    35. if (isSafe(board, i, col)) { // 检查放置在 board[i][col] 是否安全
    36. board[i][col] = 1; // 放置皇后
    37. if (solveNQUtil(board, col + 1)) // 递归放置其余皇后
    38. return true;
    39. board[i][col] = 0; // 回溯:移除皇后
    40. }
    41. }
    42. return false; // 如果无法放置皇后
    43. }
    44. // 解决 N 皇后问题
    45. bool solveNQ() {
    46. int board[N][N] = { {0, 0, 0, 0},
    47. {0, 0, 0, 0},
    48. {0, 0, 0, 0},
    49. {0, 0, 0, 0} };
    50. if (solveNQUtil(board, 0) == false) {
    51. printf("Solution does not exist");
    52. return false;
    53. }
    54. printSolution(board); // 打印解决方案
    55. return true;
    56. }
    57. int main() {
    58. solveNQ(); // 解决问题
    59. return 0;
    60. }

    四、分治算法(Divide and Conquer)

    例子:归并排序(Merge Sort)

    1. #include
    2. // 合并两个子数组
    3. void merge(int arr[], int l, int m, int r) {
    4. int n1 = m - l + 1; // 左子数组的大小
    5. int n2 = r - m; // 右子数组的大小
    6. int L[n1], R[n2]; // 临时数组
    7. // 复制数据到临时数组 L[] 和 R[]
    8. for (int i = 0; i < n1; i++)
    9. L[i] = arr[l + i];
    10. for (int j = 0; j < n2; j++)
    11. R[j] = arr[m + 1 + j];
    12. // 合并临时数组到 arr[l..r]
    13. int i = 0, j = 0, k = l;
    14. while (i < n1 && j < n2) {
    15. if (L[i] <= R[j]) {
    16. arr[k] = L[i];
    17. i++;
    18. } else {
    19. arr[k] = R[j];
    20. j++;
    21. }
    22. k++;
    23. }
    24. // 复制 L[] 剩余元素
    25. while (i < n1) {
    26. arr[k] = L[i];
    27. i++;
    28. k++;
    29. }
    30. // 复制 R[] 剩余元素
    31. while (j < n2) {
    32. arr[k] = R[j];
    33. j++;
    34. k++;
    35. }
    36. }
    37. // 归并排序
    38. void mergeSort(int arr[], int l, int r) {
    39. if (l < r) {
    40. int m = l + (r - l) / 2; // 找到中点
    41. mergeSort(arr, l, m); // 排序左半部分
    42. mergeSort(arr, m + 1, r); // 排序右半部分
    43. merge(arr, l, m, r); // 合并已排序的部分
    44. }
    45. }
    46. // 打印数组
    47. void printArray(int A[], int size) {
    48. for (int i = 0; i < size; i++)
    49. printf("%d ", A[i]);
    50. printf("\n");
    51. }
    52. int main() {
    53. int arr[] = {12, 11, 13, 5, 6, 7};
    54. int arr_size = sizeof(arr) / sizeof(arr[0]);
    55. printf("Given array is \n");
    56. printArray(arr, arr_size);
    57. mergeSort(arr, 0, arr_size - 1);
    58. printf("\nSorted array is \n");
    59. printArray(arr, arr_size);
    60. return 0;
    61. }

    五、图算法(Graph Algorithms)

    例子:Dijkstra 算法(单源最短路径)

    1. #include
    2. #include
    3. #include
    4. #define V 9 // 顶点数量
    5. // 找到最小距离的顶点
    6. int minDistance(int dist[], bool sptSet[]) {
    7. int min = INT_MAX, min_index;
    8. for (int v = 0; v < V; v++)
    9. if (sptSet[v] == false && dist[v] <= min)
    10. min = dist[v], min_index = v;
    11. return min_index;
    12. }
    13. // 打印解
    14. void printSolution(int dist[], int n) {
    15. printf("Vertex \t Distance from Source\n");
    16. for (int i = 0; i < V; i++)
    17. printf("%d \t\t %d\n", i, dist[i]);
    18. }
    19. // Dijkstra 算法
    20. void dijkstra(int graph[V][V], int src) {
    21. int dist[V]; // 最短距离数组
    22. bool sptSet[V]; // sptSet[i] 为 true 表示顶点 i 已包含在最短路径树中
    23. // 初始化所有距离为无穷大,sptSet[] 为 false
    24. for (int i = 0; i < V; i++)
    25. dist[i] = INT_MAX, sptSet[i] = false;
    26. dist[src] = 0; // 源顶点距离为 0
    27. for (int count = 0; count < V - 1; count++) {
    28. int u = minDistance(dist, sptSet); // 选取最小距离顶点
    29. sptSet[u] = true; // 标记为已处理
    30. for (int v = 0; v < V; v++)
    31. if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v])
    32. dist[v] = dist[u] + graph[u][v]; // 更新距离
    33. }
    34. printSolution(dist, V); // 打印结果
    35. }
    36. int main() {
    37. int graph[V][V] = { {0, 4, 0, 0, 0, 0, 0, 8, 0},
    38. {4, 0, 8, 0, 0, 0, 0, 11, 0},
    39. {0, 8, 0, 7, 0, 4, 0, 0, 2},
    40. {0, 0, 7, 0, 9, 14, 0, 0, 0},
    41. {0, 0, 0, 9, 0, 10, 0, 0, 0},
    42. {0, 0, 4, 14, 10, 0, 2, 0, 0},
    43. {0, 0, 0, 0, 0, 2, 0, 1, 6},
    44. {8, 11, 0, 0, 0, 0, 1, 0, 7},
    45. {0, 0, 2, 0, 0, 0, 6, 7, 0} };
    46. dijkstra(graph, 0); // 调用 Dijkstra 算法
    47. return 0;
    48. }

    贪心算法(Greedy Algorithm)

    例子:霍夫曼编码(Huffman Coding)

    1. #include
    2. #include
    3. #define MAX_TREE_HT 100
    4. // 最小堆节点
    5. struct MinHeapNode {
    6. char data; // 字符
    7. unsigned freq; // 频率
    8. struct MinHeapNode *left, *right; // 左右子节点
    9. };
    10. // 最小堆
    11. struct MinHeap {
    12. unsigned size; // 当前大小
    13. unsigned capacity; // 容量
    14. struct MinHeapNode **array; // 数组指针
    15. };
    16. // 创建新节点
    17. struct MinHeapNode* newNode(char data, unsigned freq) {
    18. struct MinHeapNode* temp = (struct MinHeapNode*)malloc(sizeof(struct MinHeapNode));
    19. temp->left = temp->right = NULL;
    20. temp->data = data;
    21. temp->freq = freq;
    22. return temp;
    23. }
    24. // 创建最小堆
    25. struct MinHeap* createMinHeap(unsigned capacity) {
    26. struct MinHeap* minHeap = (struct MinHeap*)malloc(sizeof(struct MinHeap));
    27. minHeap->size = 0;
    28. minHeap->capacity = capacity;
    29. minHeap->array = (struct MinHeapNode**)malloc(minHeap->capacity * sizeof(struct MinHeapNode*));
    30. return minHeap;
    31. }
    32. // 交换两个最小堆节点
    33. void swapMinHeapNode(struct MinHeapNode** a, struct MinHeapNode** b) {
    34. struct MinHeapNode* t = *a;
    35. *a = *b;
    36. *b = t;
    37. }
    38. // 最小堆化
    39. void minHeapify(struct MinHeap* minHeap, int idx) {
    40. int smallest = idx;
    41. int left = 2 * idx + 1;
    42. int right = 2 * idx + 2;
    43. if (left < minHeap->size && minHeap->array[left]->freq < minHeap->array[smallest]->freq)
    44. smallest = left;
    45. if (right < minHeap->size && minHeap->array[right]->freq < minHeap->array[smallest]->freq)
    46. smallest = right;
    47. if (smallest != idx) {
    48. swapMinHeapNode(&minHeap->array[smallest], &minHeap->array[idx]);
    49. minHeapify(minHeap, smallest);
    50. }
    51. }
    52. // 检查是否只有一个元素
    53. int isSizeOne(struct MinHeap* minHeap) {
    54. return (minHeap->size == 1);
    55. }
    56. // 提取最小值节点
    57. struct MinHeapNode* extractMin(struct MinHeap* minHeap) {
    58. struct MinHeapNode* temp = minHeap->array[0];
    59. minHeap->array[0] = minHeap->array[minHeap->size - 1];
    60. --minHeap->size;
    61. minHeapify(minHeap, 0);
    62. return temp;
    63. }
    64. // 插入节点到最小堆
    65. void insertMinHeap(struct MinHeap* minHeap, struct MinHeapNode* minHeapNode) {
    66. ++minHeap->size;
    67. int i = minHeap->size - 1;
    68. while (i && minHeapNode->freq < minHeap->array[(i - 1) / 2]->freq) {
    69. minHeap->array[i] = minHeap->array[(i - 1) / 2];
    70. i = (i - 1) / 2;
    71. }
    72. minHeap->array[i] = minHeapNode;
    73. }
    74. // 构建最小堆
    75. void buildMinHeap(struct MinHeap* minHeap) {
    76. int n = minHeap->size - 1;
    77. for (int i = (n - 1) / 2; i >= 0; --i)
    78. minHeapify(minHeap, i);
    79. }
    80. // 打印数组
    81. void printArr(int arr[], int n) {
    82. for (int i = 0; i < n; ++i)
    83. printf("%d", arr[i]);
    84. printf("\n");
    85. }
    86. // 检查是否是叶子节点
    87. int isLeaf(struct MinHeapNode* root) {
    88. return !(root->left) && !(root->right);
    89. }
    90. // 创建并构建最小堆
    91. struct MinHeap* createAndBuildMinHeap(char data[], int freq[], int size) {
    92. struct MinHeap* minHeap = createMinHeap(size);
    93. for (int i = 0; i < size; ++i)
    94. minHeap->array[i] = newNode(data[i], freq[i]);
    95. minHeap->size = size;
    96. buildMinHeap(minHeap);
    97. return minHeap;
    98. }
    99. // 构建霍夫曼树
    100. struct MinHeapNode* buildHuffmanTree(char data[], int freq[], int size) {
    101. struct MinHeapNode *left, *right, *top;
    102. struct MinHeap* minHeap = createAndBuildMinHeap(data, freq, size);
    103. while (!isSizeOne(minHeap)) {
    104. left = extractMin(minHeap);
    105. right = extractMin(minHeap);
    106. top = newNode('$', left->freq + right->freq);
    107. top->left = left;
    108. top->right = right;
    109. insertMinHeap(minHeap, top);
    110. }
    111. return extractMin(minHeap);
    112. }
    113. // 打印霍夫曼编码
    114. void printCodes(struct MinHeapNode* root, int arr[], int top) {
    115. if (root->left) {
    116. arr[top] = 0;
    117. printCodes(root->left, arr, top + 1);
    118. }
    119. if (root->right) {
    120. arr[top] = 1;
    121. printCodes(root->right, arr, top + 1);
    122. }
    123. if (isLeaf(root)) {
    124. printf("%c: ", root->data);
    125. printArr(arr, top);
    126. }
    127. }
    128. // 霍夫曼编码
    129. void HuffmanCodes(char data[], int freq[], int size) {
    130. struct MinHeapNode* root = buildHuffmanTree(data, freq, size);
    131. int arr[MAX_TREE_HT], top = 0;
    132. printCodes(root, arr, top);
    133. }
    134. int main() {
    135. char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
    136. int freq[] = {5, 9, 12, 13, 16, 45};
    137. int size = sizeof(arr) / sizeof(arr[0]);
    138. HuffmanCodes(arr, freq, size); // 调用霍夫曼编码
    139. return 0;
    140. }

    总结

    • 分治算法通过递归地将问题分解为子问题,解决这些子问题然后合并其解,适用于排序、搜索等问题。
    • 图算法如Dijkstra算法,通过逐步扩展最短路径树,找到图中从单个源到所有其他顶点的最短路径。
    • 贪心算法如霍夫曼编码,通过每一步选择局部最优解,最终构建出全局最优解,适用于数据压缩等问题。
    • 贪心算法每一步都选择当前最优的选择,适用于能够通过局部最优达到全局最优的问题。
    • 回溯算法系统地搜索所有可能的解,通过尝试构建解并在不满足条件时回溯,适用于需要探索所有可能解的问题。
    • 动态规划通过存储子问题的解来避免重复计算,适用于有重叠子问题和最优子结构的问题。

       
  • 相关阅读:
    io集合管理
    Linux多线程网络通信
    python究竟可以用来做些什么
    常用短信平台一览,记得收藏哦
    SpringMVC——数据提交的优化,四种跳转方式以及日期的处理
    Bert不完全手册9. 长文本建模 BigBird & Longformer & Reformer & Performer
    ipv6地址概述——配置ipv6
    利用ROS camera_calibration对usb相机标定
    SSM框架真没那么难,这份阿里大佬的进阶实战笔记真给讲透了!
    Memtester框架是什么
  • 原文地址:https://blog.csdn.net/aaaa_hsjsueu/article/details/139455724