当前位置:   article > 正文

学懂C语言(四十):C语言 数据结构与算法详解

学懂C语言(四十):C语言 数据结构与算法详解

目录

1. 数据结构概述

2. 常见算法

3. C语言实现示例

3.1 数组

3.2 链表

3.3 栈

3.4 队列

3.5 树

3.6 图

3.7 排序与查找

示例:冒泡排序

4. 总结


数据结构与算法是计算机科学的核心内容,C语言作为底层编程语言,广泛用于实现各种数据结构和算法。下面将详细讨论一些常见的数据结构及其相关算法。

1. 数据结构概述

数据结构是特定方式组织和存储数据的集合,以便高效地访问和修改。常见的数据结构包括:

  • 线性数据结构

    • 数组:固定大小的元素集合,支持随机访问。
    • 链表:节点组成的集合,每个节点包含数据和指向下一个节点的指针。
      • 单链表:每个节点指向下一个节点。
      • 双链表:每个节点指向前一个和后一个节点。
      • 循环链表:最后一个节点指向第一个节点。
  • :后进先出(LIFO)结构。常用于函数调用管理、表达式求值等。

    • 操作:push(入栈)、pop(出栈)、peek(查看栈顶元素)。
  • 队列:先进先出(FIFO)结构。常用于任务调度、缓冲区管理等。

    • 操作:enqueue(入队)、dequeue(出队)、front(查看队首元素)。
    • 二叉树:每个节点最多有两个子节点。
    • 二叉搜索树:左子树所有节点小于根节点,右子树所有节点大于根节点。
    • 平衡树:如 AVL 树、红黑树,保持树的平衡性以提高查询效率。
    • :完全二叉树,满足堆性质(最大堆或最小堆)。
  • :由顶点和边组成的结构,表示对象之间的关系。

    • 表示方式:邻接矩阵、邻接表。
  • 哈希表:通过哈希函数将键映射到数组索引,提供快速的查找和插入。

2. 常见算法

算法是对解决特定问题的步骤和逻辑的描述。常见算法包括:

  • 排序算法

    • 冒泡排序:重复遍历数组,比较相邻元素并交换,直到排序完成。
    • 选择排序:每次选择最小的元素,将其放到已排序部分的末尾。
    • 插入排序:将数组分为已排序和未排序部分,逐个插入未排序元素。
    • 快速排序:选择基准元素,将数组分为小于和大于基准的两部分,递归排序。
    • 归并排序:分治法将数组分为两部分,分别排序后合并。
  • 查找算法

    • 线性查找:逐个比较,找到目标元素。
    • 二分查找:在已排序数组中,通过不断将搜索范围减半来查找目标元素。
  • 图算法

    • 深度优先搜索(DFS):从一个节点出发,尽可能深入到每个分支,再回溯。
    • 广度优先搜索(BFS):从一个节点出发,先访问所有相邻节点,再从这些节点访问它们的邻居。
    • Dijkstra 算法:求解单源最短路径的贪心算法。
    • A 算法*:启发式搜索算法,常用于路径寻找。
  • 动态规划

    • 解决最优子结构问题,通过保存子问题的结果来减少计算。
    • 常见问题:斐波那契数列、背包问题、最长公共子序列等。

3. C语言实现示例

3.1 数组

数组是一种线性数据结构,支持随机访问。

示例:数组求和

  1. #include <stdio.h>
  2. int main() {
  3. int arr[] = {1, 2, 3, 4, 5};
  4. int sum = 0;
  5. int n = sizeof(arr) / sizeof(arr[0]);
  6. for (int i = 0; i < n; i++) {
  7. sum += arr[i]; // 累加
  8. }
  9. printf("数组元素的和是: %d\n", sum);
  10. return 0;
  11. }
3.2 链表

链表是一种动态数据结构,由节点组成,每个节点包含数据和指向下一个节点的指针。

示例:单链表的基本操作

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 节点结构
  4. struct Node {
  5. int data;
  6. struct Node* next;
  7. };
  8. // 创建新节点
  9. struct Node* createNode(int data) {
  10. struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
  11. newNode->data = data;
  12. newNode->next = NULL;
  13. return newNode;
  14. }
  15. // 打印链表
  16. void printList(struct Node* head) {
  17. struct Node* temp = head;
  18. while (temp != NULL) {
  19. printf("%d -> ", temp->data);
  20. temp = temp->next;
  21. }
  22. printf("NULL\n");
  23. }
  24. // 插入节点
  25. void insertAtEnd(struct Node** head, int data) {
  26. struct Node* newNode = createNode(data);
  27. if (*head == NULL) {
  28. *head = newNode;
  29. return;
  30. }
  31. struct Node* temp = *head;
  32. while (temp->next != NULL) {
  33. temp = temp->next;
  34. }
  35. temp->next = newNode;
  36. }
  37. int main() {
  38. struct Node* head = NULL;
  39. insertAtEnd(&head, 1);
  40. insertAtEnd(&head, 2);
  41. insertAtEnd(&head, 3);
  42. printList(head); // 输出: 1 -> 2 -> 3 -> NULL
  43. return 0;
  44. }
3.3 

栈是一种后进先出(LIFO)结构,常用于函数调用管理。

示例:栈的实现

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX 100
  4. struct Stack {
  5. int top;
  6. int arr[MAX];
  7. };
  8. // 初始化栈
  9. struct Stack* createStack() {
  10. struct Stack* stack = (struct Stack*)malloc(sizeof(struct Stack));
  11. stack->top = -1; // 栈为空
  12. return stack;
  13. }
  14. // 判断栈是否为空
  15. int isEmpty(struct Stack* stack) {
  16. return stack->top == -1;
  17. }
  18. // 入栈
  19. void push(struct Stack* stack, int item) {
  20. if (stack->top == MAX - 1) {
  21. printf("栈溢出\n");
  22. return;
  23. }
  24. stack->arr[++stack->top] = item;
  25. }
  26. // 出栈
  27. int pop(struct Stack* stack) {
  28. if (isEmpty(stack)) {
  29. printf("栈为空\n");
  30. return -1;
  31. }
  32. return stack->arr[stack->top--];
  33. }
  34. // 查看栈顶元素
  35. int peek(struct Stack* stack) {
  36. if (isEmpty(stack)) {
  37. printf("栈为空\n");
  38. return -1;
  39. }
  40. return stack->arr[stack->top];
  41. }
  42. int main() {
  43. struct Stack* stack = createStack();
  44. push(stack, 1);
  45. push(stack, 2);
  46. push(stack, 3);
  47. printf("栈顶元素是: %d\n", peek(stack)); // 输出: 3
  48. printf("出栈元素: %d\n", pop(stack)); // 输出: 3
  49. printf("栈顶元素是: %d\n", peek(stack)); // 输出: 2
  50. return 0;
  51. }
3.4 队列

队列是一种先进先出(FIFO)结构,常用于任务调度和缓冲区管理。

示例:队列的实现

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX 100
  4. struct Queue {
  5. int front, rear;
  6. int arr[MAX];
  7. };
  8. // 初始化队列
  9. struct Queue* createQueue() {
  10. struct Queue* queue = (struct Queue*)malloc(sizeof(struct Queue));
  11. queue->front = queue->rear = -1; // 队列为空
  12. return queue;
  13. }
  14. // 判断队列是否为空
  15. int isEmpty(struct Queue* queue) {
  16. return queue->front == -1;
  17. }
  18. // 入队
  19. void enqueue(struct Queue* queue, int item) {
  20. if (queue->rear == MAX - 1) {
  21. printf("队列满\n");
  22. return;
  23. }
  24. if (isEmpty(queue)) {
  25. queue->front = 0;
  26. }
  27. queue->arr[++queue->rear] = item;
  28. }
  29. // 出队
  30. int dequeue(struct Queue* queue) {
  31. if (isEmpty(queue)) {
  32. printf("队列为空\n");
  33. return -1;
  34. }
  35. int item = queue->arr[queue->front];
  36. if (queue->front >= queue->rear) {
  37. queue->front = queue->rear = -1; // 队列空了
  38. } else {
  39. queue->front++;
  40. }
  41. return item;
  42. }
  43. int main() {
  44. struct Queue* queue = createQueue();
  45. enqueue(queue, 1);
  46. enqueue(queue, 2);
  47. enqueue(queue, 3);
  48. printf("出队元素: %d\n", dequeue(queue)); // 输出: 1
  49. printf("出队元素: %d\n", dequeue(queue)); // 输出: 2
  50. return 0;
  51. }
3.5 

树是一种分层数据结构,常用于表示层级关系。

示例:二叉树的遍历

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. struct Node {
  4. int data;
  5. struct Node* left;
  6. struct Node* right;
  7. };
  8. // 创建新节点
  9. struct Node* createNode(int data) {
  10. struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
  11. newNode->data = data;
  12. newNode->left = newNode->right = NULL;
  13. return newNode;
  14. }
  15. // 先序遍历
  16. void preOrder(struct Node* root) {
  17. if (root != NULL) {
  18. printf("%d ", root->data);
  19. preOrder(root->left);
  20. preOrder(root->right);
  21. }
  22. }
  23. // 中序遍历
  24. void inOrder(struct Node* root) {
  25. if (root != NULL) {
  26. inOrder(root->left);
  27. printf("%d ", root->data);
  28. inOrder(root->right);
  29. }
  30. }
  31. // 后序遍历
  32. void postOrder(struct Node* root) {
  33. if (root != NULL) {
  34. postOrder(root->left);
  35. postOrder(root->right);
  36. printf("%d ", root->data);
  37. }
  38. }
  39. int main() {
  40. struct Node* root = createNode(1);
  41. root->left = createNode(2);
  42. root->right = createNode(3);
  43. root->left->left = createNode(4);
  44. root->left->right = createNode(5);
  45. printf("先序遍历: ");
  46. preOrder(root); // 输出: 1 2 4 5 3
  47. printf("\n");
  48. printf("中序遍历: ");
  49. inOrder(root); // 输出: 4 2 5 1 3
  50. printf("\n");
  51. printf("后序遍历: ");
  52. postOrder(root); // 输出: 4 5 2 3 1
  53. printf("\n");
  54. return 0;
  55. }
3.6 

图是由顶点和边组成的结构,表示对象之间的关系。

示例:邻接表表示的图

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. struct Node {
  4. int dest;
  5. struct Node* next;
  6. };
  7. struct Graph {
  8. int vertices;
  9. struct Node** adjLists;
  10. };
  11. // 创建图
  12. struct Graph* createGraph(int vertices) {
  13. struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
  14. graph->vertices = vertices;
  15. graph->adjLists = (struct Node**)malloc(vertices * sizeof(struct Node*));
  16. for (int i = 0; i < vertices; i++) {
  17. graph->adjLists[i] = NULL;
  18. }
  19. return graph;
  20. }
  21. // 添加边
  22. void addEdge(struct Graph* graph, int src, int dest) {
  23. struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
  24. newNode->dest = dest;
  25. newNode->next = graph->adjLists[src];
  26. graph->adjLists[src] = newNode;
  27. }
  28. // 打印图
  29. void printGraph(struct Graph* graph) {
  30. for (int v = 0; v < graph->vertices; v++) {
  31. struct Node* temp = graph->adjLists[v];
  32. printf("\n 对于顶点 %d: ", v);
  33. while (temp) {
  34. printf("%d -> ", temp->dest);
  35. temp = temp->next;
  36. }
  37. printf("NULL");
  38. }
  39. }
  40. int main() {
  41. struct Graph* graph = createGraph(5);
  42. addEdge(graph, 0, 1);
  43. addEdge(graph, 0, 4);
  44. addEdge(graph, 1, 2);
  45. addEdge(graph, 1, 3);
  46. addEdge(graph, 1, 4);
  47. addEdge(graph, 2, 3);
  48. addEdge(graph, 3, 4);
  49. printGraph(graph); // 打印图的邻接表表示
  50. return 0;
  51. }
3.7 排序与查找

排序与查找是常见的算法问题,涉及如何组织数据以便于访问。

示例:快速排序

  1. #include <stdio.h>
  2. void swap(int* a, int* b) {
  3. int temp = *a;
  4. *a = *b;
  5. *b = temp;
  6. }
  7. int partition(int arr[], int low, int high) {
  8. int pivot = arr[high];
  9. int i = (low - 1);
  10. for (int j = low; j < high; j++) {
  11. if (arr[j] < pivot) {
  12. i++;
  13. swap(&arr[i], &arr[j]);
  14. }
  15. }
  16. swap(&arr[i + 1], &arr[high]);
  17. return (i + 1);
  18. }
  19. void quickSort(int arr[], int low, int high) {
  20. if (low < high) {
  21. int pi = partition(arr, low, high);
  22. quickSort(arr, low, pi - 1);
  23. quickSort(arr, pi + 1, high);
  24. }
  25. }
  26. int main() {
  27. int arr[] = {10, 7, 8, 9, 1, 5};
  28. int n = sizeof(arr) / sizeof(arr[0]);
  29. quickSort(arr, 0, n - 1);
  30. printf("排序后的数组: \n");
  31. for (int i = 0; i < n; i++) {
  32. printf("%d ", arr[i]);
  33. }
  34. return 0;
  35. }
示例:冒泡排序
  1. #include <stdio.h>
  2. void bubbleSort(int arr[], int n) {
  3. for (int i = 0; i < n - 1; i++) {
  4. for (int j = 0; j < n - i - 1; j++) {
  5. if (arr[j] > arr[j + 1]) {
  6. // 交换 arr[j] 和 arr[j+1]
  7. int temp = arr[j];
  8. arr[j] = arr[j + 1];
  9. arr[j + 1] = temp;
  10. }
  11. }
  12. }
  13. }
  14. int main() {
  15. int arr[] = {64, 34, 25, 12, 22, 11, 90};
  16. int n = sizeof(arr) / sizeof(arr[0]);
  17. bubbleSort(arr, n);
  18. printf("排序后的数组: \n");
  19. for (int i = 0; i < n; i++) {
  20. printf("%d ", arr[i]);
  21. }
  22. return 0;
  23. }

4. 总结

通过以上实例,可以更好地理解 C 语言中的数据结构与算法。这些结构与算法是编程的基石,掌握它们对于解决复杂问题至关重要。建议通过实际编程练习、参加算法竞赛等方式,不断提升自己的能力和经验。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/952017
推荐阅读
相关标签
  

闽ICP备14008679号