数据结构小白必看:手把手教你用C语言实现PTA题库中的经典算法

张开发
2026/4/14 23:10:30 15 分钟阅读

分享文章

数据结构小白必看:手把手教你用C语言实现PTA题库中的经典算法
数据结构小白必看手把手教你用C语言实现PTA题库中的经典算法第一次接触数据结构时看着课本上那些抽象的概念和复杂的图示我完全摸不着头脑。直到在PTA题库里反复练习那些经典算法才真正理解了数据结构的精髓。这篇文章将带你用最接地气的方式从零开始实现那些让无数初学者头疼的数据结构算法。1. 顺序表数据结构的入门基石顺序表就像一排连续的储物柜每个格子都有固定编号。在C语言中我们用数组来实现这种线性结构。先来看一个最简单的例子——顺序查找int sequentialSearch(int arr[], int size, int target) { for (int i 0; i size; i) { if (arr[i] target) { return i; // 找到返回索引 } } return -1; // 未找到 }顺序表的插入操作需要特别注意元素的后移#define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int length; } SeqList; int insert(SeqList *L, int index, int value) { if (L-length MAX_SIZE || index 0 || index L-length) { return 0; // 插入失败 } for (int i L-length; i index; i--) { L-data[i] L-data[i-1]; // 元素后移 } L-data[index] value; L-length; return 1; // 插入成功 }顺序表操作要点插入/删除时间复杂度为O(n)随机访问时间复杂度为O(1)适合元素数量固定、查询频繁的场景2. 链表灵活的动态数据结构当我们需要频繁插入删除时链表就派上用场了。单链表的基本结构如下typedef struct Node { int data; struct Node *next; } ListNode;头插法创建链表是PTA常考题型ListNode* createListHead(int arr[], int n) { ListNode *head (ListNode*)malloc(sizeof(ListNode)); head-next NULL; for (int i 0; i n; i) { ListNode *node (ListNode*)malloc(sizeof(ListNode)); node-data arr[i]; node-next head-next; head-next node; } return head; }链表删除操作要特别注意指针的修改顺序int deleteNode(ListNode *head, int index) { ListNode *p head; int i 0; while (p-next i index) { p p-next; i; } if (!p-next) return 0; // 删除失败 ListNode *temp p-next; p-next temp-next; free(temp); return 1; // 删除成功 }提示链表操作最容易出现内存泄漏记得每次删除节点后都要free3. 栈与队列受限的线性表栈是后进先出(LIFO)的结构十进制转二进制是栈的经典应用#define STACK_SIZE 100 int stack[STACK_SIZE]; int top -1; void push(int x) { if (top STACK_SIZE - 1) return; stack[top] x; } int pop() { if (top -1) return -1; return stack[top--]; } void decimalToBinary(int num) { while (num 0) { push(num % 2); num / 2; } while (top ! -1) { printf(%d, pop()); } }循环队列解决假溢出问题#define QUEUE_SIZE 5 typedef struct { int data[QUEUE_SIZE]; int front, rear; } CircularQueue; void initQueue(CircularQueue *q) { q-front q-rear 0; } int enQueue(CircularQueue *q, int x) { if ((q-rear 1) % QUEUE_SIZE q-front) return 0; // 队满 q-data[q-rear] x; q-rear (q-rear 1) % QUEUE_SIZE; return 1; }4. 二叉树递归的艺术二叉树遍历是理解递归的最佳案例。先序遍历的递归实现typedef struct TreeNode { char data; struct TreeNode *left, *right; } TreeNode; void preOrder(TreeNode *root) { if (root) { printf(%c , root-data); preOrder(root-left); preOrder(root-right); } }计算二叉树高度展示递归的威力int treeHeight(TreeNode *root) { if (!root) return 0; int left treeHeight(root-left); int right treeHeight(root-right); return (left right ? left : right) 1; }二叉树操作要点递归终止条件必须明确先处理当前节点再递归处理子树时间复杂度通常为O(n)5. 查找算法从暴力到优雅顺序查找简单直接但效率低下int seqSearch(int arr[], int n, int key) { for (int i 0; i n; i) { if (arr[i] key) return i; } return -1; }二分查找要求数组有序但效率极高int binarySearch(int arr[], int n, int key) { int left 0, right n - 1; while (left right) { int mid left (right - left) / 2; if (arr[mid] key) return mid; else if (arr[mid] key) left mid 1; else right mid - 1; } return -1; }注意二分查找的数组必须是有序的否则结果不可预测在实际刷PTA题库时我发现最常犯的错误就是边界条件处理不当。比如二分查找中的left right判断或者链表操作时的空指针检查。建议每个算法都先用小数据测试各种边界情况。

更多文章