c语言程序设计数据结构应用:链表栈队列实现教程,附实战项目

admin C语言 5


学了 C 语言语法,一碰到数据结构就懵?想用数组存数据,结果数据太多存不下;想删个中间的元素,数组得挪半天;更别说处理括号匹配、消息排队这些问题,根本不知道用啥结构 —— 你是不是也觉得数据结构比纯语法难多了?其实啊,链表、栈、队列这三个 “老熟人”,学会了能解决大半问题。它们就像不同的容器,各有各的用法,用对了能省老多事。兔子哥当年学这些的时候,对着指针绕了好几天,后来结合实例才搞明白,今天就用大白话讲讲怎么用 C 语言实现,再附个实战项目,一起往下看吧!

一、为啥非得学这些结构?数组不够用吗?


可能有人会说,我用数组挺好的,为啥还要学链表、栈、队列?这就得说说数组的 “短板” 了。
数组得先确定大小,比如int a[100],最多存 100 个数据,万一不够了,改起来特别麻烦;想在中间插个数据,得把后面的全往后挪,费时费力。而链表、栈、队列这些结构,就灵活多了 —— 链表能随便加数据,栈和队列能按特定顺序处理数据,各有各的神通。
就像装东西,数组是固定大小的箱子,满了就塞不下;链表是能无限接长的链条,想加多少加多少;栈是只能从顶上放、顶上拿的抽屉;队列是排队买票,先来的先处理。不同场景用不同的 “容器”,这就是数据结构的门道。


二、链表:像串珠子一样存数据,增删改特别方便


链表是最灵活的,由一个个 “节点” 串起来,每个节点分两部分:存数据的 “数据域”,和指向下一个节点的 “指针域”。

1. 先定义节点结构,这是基础


c运行
// 定义一个存整数的链表节点struct Node {int data;          // 数据域,存具体数值struct Node *next; // 指针域,指向下一个节点};

就像一颗珠子,data 是珠子里的东西,next 是穿珠子的线,指向后面那颗珠子。

2. 怎么往链表加数据?头插法最简单


头插法就是往链表的最前面加节点,步骤大概是:
  • 新造一个节点,给 data 赋值;
  • 让新节点的 next 指向原来的头节点;
  • 把头节点换成新节点。

代码大概这样(带注释):
c运行
// 头插法添加节点struct Node* addHead(struct Node *head, int num) {// 1. 申请一块内存给新节点struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));if(newNode == NULL) { // 万一内存不够,得处理下printf("内存不够啦");return head;}// 2. 给新节点存数据newNode->data = num;// 3. 新节点的next指向原来的头newNode->next = head;// 4. 头变成新节点head = newNode;return head;}

刚开始写的时候,总搞反 newNode->next 和 head 的关系,画个图就清楚了 —— 新节点插在最前面,自然要指向原来的头。

3. 删节点、查节点,关键是找对位置


删节点得先找到要删的节点的前一个,让前一个的 next 跳过要删的,直接指向下一个;查节点就从头往后遍历,一个个比 data。这些操作看着麻烦,多练两次就顺了。


三、栈:只能从 “栈顶” 进出,像叠盘子一样


栈有个特点:先进后出,就像叠盘子,最后放的那个得先拿下来。

1. 用数组实现栈,简单好懂


定义一个数组存数据,再用个变量 top 记着栈顶位置:
c运行
#define MAX_SIZE 100int stack[MAX_SIZE];int top = -1; // 栈顶索引,-1表示空栈

2. 入栈和出栈,就俩核心操作


  • 入栈:top 加 1,然后把数据放进去,注意别超过 MAX_SIZE;

c运行
void push(int data) {if(top >= MAX_SIZE - 1) { // 栈满了printf("栈满啦,放不下");return;}stack[++top] = data; // 先加top,再存数据}

  • 出栈:先拿 top 位置的数据,再把 top 减 1;

c运行
int pop() {if(top == -1) { // 栈空了printf("栈空啦,没数据");return -1; // 用-1表示出错}return stack[top--]; // 先拿数据,再减top}

当年我总搞反 push 里的++toptop++,后来发现++top是先移栈顶再存,才对,不然第一个数据会存在索引 0 的位置。


四、队列:先进先出,像排队买票


队列跟栈正好相反,先进先出,就像排队,先来的先买票。

1. 用数组实现队列,得记头和尾


需要两个变量:front(队头,拿数据的地方)和 rear(队尾,放数据的地方):
c运行
#define MAX_QUEUE 100int queue[MAX_QUEUE];int front = 0;int rear = 0;

2. 入队和出队,注意 “循环” 问题


  • 入队:数据放 rear 位置,rear 加 1,超过最大长度就绕回 0(循环队列);

c运行
void enqueue(int data) {int nextRear = (rear + 1) % MAX_QUEUE;if(nextRear == front) { // 队满了printf("队满啦,进不来");return;}queue[rear] = data;rear = nextRear;}

  • 出队:拿 front 位置的数据,front 加 1,同样绕回 0;

c运行
int dequeue() {if(front == rear) { // 队空了printf("队空啦,没数据");return -1;}int data = queue[front];front = (front + 1) % MAX_QUEUE;return data;}

循环队列刚开始理解起来有点绕,其实就是让队列 “首尾相连”,不浪费空间,画个圈就能看明白。
数据结构特点最适合的场景C 语言实现难度
链表可动态增减,灵活数据量不确定,常增删稍难(指针多)
先进后出括号匹配、函数调用、撤销操作简单
队列先进先出消息处理、排队系统中等



五、实战项目:简易计算器(用栈处理表达式)


用栈来做个能算加减乘除的计算器,核心是用栈存数字和运算符,处理运算顺序。

1. 思路:


  • 遇到数字直接入栈;
  • 遇到运算符,跟栈顶的运算符比优先级,优先级高就入栈,低就把栈顶的运算符弹出来算;
  • 最后把栈里剩下的运算符弹出来算。

2. 核心代码片段(处理运算符部分):


c运行
// 简化版:比较运算符优先级int priority(char op) {if(op == '+' || op == '-') return 1;if(op == '*' || op == '/') return 2;return 0;}// 处理运算符入栈void handleOp(char op, int *numStack, int *numTop, char *opStack, int *opTop) {// 栈顶运算符优先级高,先算while(*opTop != -1 && priority(opStack[*opTop]) >= priority(op)) {char topOp = opStack[(*opTop)--]; // 弹出运算符int b = numStack[(*numTop)--];    // 弹出两个数字int a = numStack[(*numTop)--];int res;if(topOp == '+') res = a + b;else if(topOp == '-') res = a - b;else if(topOp == '*') res = a * b;else res = a / b;numStack[++(*numTop)] = res; // 结果入栈}opStack[++(*opTop)] = op; // 当前运算符入栈}

这个项目能把栈的用法练透,刚开始可能算不对,多调几次就好了,我当年改了五遍才让它正确算 “3+5*2” 这种带优先级的表达式。


可能有人会问,这些结构在实际项目中用得多吗?太多了!操作系统里的进程调度用队列,编译器查括号用栈,链表更是到处都是。
兔子哥觉得,学这些别光看代码,得多画图 —— 链表怎么串的,栈怎么进出的,一画就明白。刚开始写指针总出错很正常,我当年写链表删除节点,总忘了释放内存,导致内存泄漏,后来才记住 free () 函数。多敲代码,从简单的增删改查做起,慢慢就找到感觉了。希望这些能帮到你,赶紧动手试试吧!

标签: 数据结构 串珠子

发布评论 0条评论)

  • Refresh code

还木有评论哦,快来抢沙发吧~