链表还会用吗?用链表实现栈(附算法源码)
链表是一种灵活的数据结构,适合用于需要频繁插入和删除操作的场景,但在需要快速随机访问的情况下,数组或其它数据结构更为合适。
特点
- 动态大小
链表可以根据需要动态增长或缩小,不需要事先定义大小。
- 非连续存储
链表中的节点在内存中不必连续存储,每个节点通过指针连接。
- 插入和删除效率高
在链表中插入或删除节点的时间复杂度为 O(1),只需调整指针即可,而在数组中可能需要移动大量元素,时间复杂度为 O(n)。
- 访问效率低
链表的随机访问效率较低,要访问某个节点需要从头遍历,时间复杂度为 O(n)。
适用场景
- 实现队列和栈
链表可以方便地实现队列(FIFO)和栈(LIFO)等数据结构,因为它们需要频繁的插入和删除操作。
- 动态数据存储
当数据量不确定且需要频繁变化时,链表是一种合适的选择,例如实现动态数组的替代品。
- 图和树的实现
链表可用于表示图的邻接表和树的节点结构,方便存储和遍历。
- 内存管理
在需要频繁分配和释放内存的应用中,链表可以有效管理内存,避免内存碎片。
使用示例
通过链表的结点来表示栈中的元素。入栈push操作将新元素添加到链表的头部,出栈pop操作则移除链表的头部元素。实现了一个后进先出(LIFO)的栈结构。
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点结构
typedef struct Node {
int data;
struct Node *next;
} Node;
// 定义栈结构
typedef struct Stack {
Node *top;
} Stack;
// 初始化栈
void initStack(Stack *stack) {
stack->top = NULL;
}
// 检查栈是否为空
int isEmpty(Stack *stack) {
return stack->top == NULL;
}
// 入栈操作
void push(Stack *stack, int value) {
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->data = value;
newNode->next = stack->top;
stack->top = newNode;
}
// 出栈操作
int pop(Stack *stack) {
if (isEmpty(stack)) {
printf("Stack is empty.\n");
return -1;
}
Node *temp = stack->top;
int value = temp->data;
stack->top = stack->top->next;
free(temp);
return value;
}
// 释放栈内存
void freeStack(Stack *stack) {
while (!isEmpty(stack)) {
pop(stack);
}
}
int main() {
Stack stack;
initStack(&stack);
push(&stack, 10);
push(&stack, 20);
push(&stack, 30);
printf("Popped: %d\n", pop(&stack));
printf("Popped: %d\n", pop(&stack));
printf("Popped: %d\n", pop(&stack));
freeStack(&stack);
return 0;
}