C语言数据结构入门教程:链表、栈与队列
一、前言
数据结构是计算机科学中一个非常重要的概念,它描述了数据的组织、存储和操作方式。C语言作为一种高效的编程语言,非常适合实现各种数据结构。本文将从零基础开始,详细介绍链表、栈和队列这三种常见的线性数据结构,帮助初学者快速入门。
二、链表
(一)链表的定义与特点
链表是一种动态数据结构,由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。链表的特点如下:
• 动态性:链表的大小可以根据需要动态调整,不像数组那样需要预先分配固定大小的内存。
• 灵活性:插入和删除操作非常灵活,只需修改指针即可,无需移动大量数据。
• 缺点:随机访问效率低,需要从头开始逐个遍历节点。
链表有多种类型,包括单链表、双链表和循环链表。
(二)单链表的实现
1.节点定义
typedef struct Node {
int data; // 数据域
struct Node* next; // 指针域,指向下一个节点
} Node;
2.初始化链表
Node* initList() {
Node* head = (Node*)malloc(sizeof(Node)); // 创建头节点
head->next = NULL; // 初始化为空链表
return head;
}
3.插入节点
void insertNode(Node* head, int pos, int value) {
Node* newNode = (Node*)malloc(sizeof(Node)); // 创建新节点
newNode->data = value;
Node* p = head;
for (int i = 0; i < pos; i++) { // 找到插入位置的前一个节点
p = p->next;
}
newNode->next = p->next; // 新节点指向下一个节点
p->next = newNode; // 前一个节点指向新节点
}
4.删除节点
void deleteNode(Node* head, int pos) {
Node* p = head;
for (int i = 0; i < pos; i++) { // 找到删除位置的前一个节点
p = p->next;
}
Node* temp = p->next; // 保存要删除的节点
p->next = temp->next; // 前一个节点指向下一个节点
free(temp); // 释放删除的节点
}
5.遍历链表
void traverseList(Node* head) {
Node* p = head->next; // 跳过头节点
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
(三)链表的应用场景
链表常用于实现动态数据集合,如动态数组、栈、队列等。它的灵活性使其在需要频繁插入和删除的场景中表现出色。
三、栈
(一)栈的定义与特点
栈是一种后进先出(LIFO)的线性数据结构,只允许在一端(栈顶)进行插入和删除操作。栈的特点如下:
• 后进先出:最后插入的元素最先被删除。
• 操作简单:主要操作包括入栈(Push)、出栈(Pop)和查看栈顶元素(Top)。
• 应用场景:括号匹配、递归实现、回溯算法等。
(二)栈的实现
栈可以用数组或链表实现。以下是基于链表的栈实现:
1.栈结构定义
typedef struct {
Node* top; // 栈顶指针
} Stack;
2.初始化栈
void initStack(Stack* s) {
s->top = NULL;
}
3.入栈操作
void push(Stack* s, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = s->top; // 新节点指向当前栈顶
s->top = newNode; // 更新栈顶为新节点
}
4.出栈操作
int pop(Stack* s) {
if (s->top == NULL) {
printf("Stack is empty!\n");
return -1;
}
Node* temp = s->top; // 保存栈顶节点
int value = temp->data; // 获取栈顶元素
s->top = temp->next; // 更新栈顶为下一个节点
free(temp); // 释放原栈顶节点
return value;
}
5.查看栈顶元素
int top(Stack* s) {
if (s->top == NULL) {
printf("Stack is empty!\n");
return -1;
}
return s->top->data;
}
(三)栈的应用场景
栈常用于解决括号匹配问题、递归实现、回溯算法等。例如,编译器利用栈来处理函数调用和返回。
四、队列
(一)队列的定义与特点
队列是一种先进先出(FIFO)的线性数据结构,允许在一端(队尾)插入元素,在另一端(队首)删除元素。队列的特点如下:
• 先进先出:最先插入的元素最先被删除。
• 操作简单:主要操作包括入队(Enqueue)、出队(Dequeue)和查看队首元素(Front)。
• 应用场景:任务调度、缓冲区管理、广度优先搜索等。
(二)队列的实现
队列可以用数组或链表实现。以下是基于链表的队列实现:
1.队列结构定义
typedef struct {
Node* front; // 队首指针
Node* rear; // 队尾指针
} Queue;
2.初始化队列
void initQueue(Queue* q) {
q->front = q->rear = NULL;
}
3.入队操作
void enqueue(Queue* q, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = NULL;
if (q->rear == NULL) { // 队列为空
q->front = q->rear = newNode;
} else {
q->rear->next = newNode; // 将新节点连接到队尾
q->rear = newNode; // 更新队尾指针
}
}
4.出队操作
int dequeue(Queue* q) {
if (q->front == NULL) {
printf("Queue is empty!\n");
return -1;
}
Node* temp = q->front; // 保存队首节点
int value = temp->data; // 获取队首元素
q->front = temp->next; // 更新队首指针
if (q->front == NULL) { // 如果队列为空,更新队尾指针
q->rear = NULL;
}
free(temp); // 释放原队首节点
return value;
}
5.查看队首元素
int front(Queue* q) {
if (q->front == NULL) {
printf("Queue is empty!\n");
return -1;
}
return q->front->data;
}
(三)队列的应用场景
队列常用于任务调度、缓冲区管理、广度优先搜索等。例如,操作系统利用队列来管理进程调度。
五、参考代码
完整的参考代码,供参考和实践:
单链表代码
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;
Node* initList() {
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
void insertNode(Node* head, int pos, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
Node* p = head;
for (int i = 0; i < pos; i++) {
if (p->next == NULL) {
printf("Position out of range!\n");
free(newNode);
return;
}
p = p->next;
}
newNode->next = p->next;
p->next = newNode;
}
void deleteNode(Node* head, int pos) {
Node* p = head;
for (int i = 0; i < pos; i++) {
if (p->next == NULL) {
printf("Position out of range!\n");
return;
}
p = p->next;
}
if (p->next == NULL) {
printf("Position out of range!\n");
return;
}
Node* temp = p->next;
p->next = temp->next;
free(temp);
}
void traverseList(Node* head) {
Node* p = head->next;
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main() {
Node* head = initList();
insertNode(head, 0, 10);
insertNode(head, 1, 20);
insertNode(head, 2, 30);
printf("List: ");
traverseList(head);
deleteNode(head, 1);
printf("After deletion: ");
traverseList(head);
return 0;
}
栈的代码实现
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;
typedef struct {
Node* top;
} Stack;
void initStack(Stack* s) {
s->top = NULL;
}
void push(Stack* s, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = s->top;
s->top = newNode;
}
int pop(Stack* s) {
if (s->top == NULL) {
printf("Stack is empty!\n");
return -1;
}
Node* temp = s->top;
int value = temp->data;
s->top = temp->next;
free(temp);
return value;
}
int top(Stack* s) {
if (s->top == NULL) {
printf("Stack is empty!\n");
return -1;
}
return s->top->data;
}
int main() {
Stack s;
initStack(&s);
push(&s, 10);
push(&s, 20);
push(&s, 30);
printf("Top element: %d\n", top(&s));
printf("Popped element: %d\n", pop(&s));
printf("Top element after pop: %d\n", top(&s));
return 0;
}
队列的代码实现
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;
typedef struct {
Node* front;
Node* rear;
} Queue;
void initQueue(Queue* q) {
q->front = q->rear = NULL;
}
void enqueue(Queue* q, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = NULL;
if (q->rear == NULL) {
q->front = q->rear = newNode;
} else {
q->rear->next = newNode;
q->rear = newNode;
}
}
int dequeue(Queue* q) {
if (q->front == NULL) {
printf("Queue is empty!\n");
return -1;
}
Node* temp = q->front;
int value = temp->data;
q->front = temp->next;
if (q->front == NULL) {
q->rear = NULL;
}
free(temp);
return value;
}
int front(Queue* q) {
if (q->front == NULL) {
printf("Queue is empty!\n");
return -1;
}
return q->front->data;
}
int main() {
Queue q;
initQueue(&q);
enqueue(&q, 10);
enqueue(&q, 20);
enqueue(&q, 30);
printf("Front element: %d\n", front(&q));
printf("Dequeued element: %d\n", dequeue(&q));
printf("Front element after dequeue: %d\n", front(&q));
return 0;
}
七、拓展
拓展练习:
• 链表的逆序:实现一个函数,将链表的顺序反转。
• 栈的最小值操作:扩展栈的功能,使其支持在常数时间内获取当前栈的最小值。
• 队列的优先级实现:实现一个优先级队列,支持按优先级出队。
如果有任何问题或需要进一步的解释,请随时提问!