目录
一 简介
栈是一种线性数据结构,遵循“后进先出”(Last In First Out,简称LIFO)原则。在栈中,数据的插入和删除操作只允许在表的一端进行,这一端被称为栈顶。如同现实中的栈板,最后放入的元素最先被取出。主要操作包括压栈(Push,将元素添加到栈顶)、弹栈(Pop,移除并返回栈顶元素)以及查看栈顶元素(Top)。栈常用于函数调用、表达式求值、括号匹配等计算机科学领域的多种问题解决中。
二 栈的抽象数据类型(C语言实现)
在C语言中,因为没有内置的栈数据结构,所以我们需要通过定义结构体来模拟栈的行为,并通过函数来实现栈的各种操作。以下是一个简单的栈抽象数据类型的C语言定义示例:
#include <stdio.h>
// 定义栈元素类型
typedef int ElementType;
// 定义栈节点结构体(如果采用链式栈)
typedef struct StackNode {
ElementType data;
struct StackNode* next;
} StackNode;
// 定义栈结构体(此处使用顺序栈的结构)
typedef struct {
ElementType* elements; // 存储元素的数组
int capacity; // 栈的容量
int top; // 栈顶指针,指向栈顶元素的位置
} Stack;
// 初始化栈
void initStack(Stack* s, int initialCapacity);
// 销毁栈(释放内存)
void destroyStack(Stack* s);
// 入栈操作
bool push(Stack* s, ElementType item);
// 出栈操作,同时返回栈顶元素
ElementType pop(Stack* s);
// 查看栈顶元素而不移除
ElementType peek(Stack* s);
// 判断栈是否为空
bool isEmpty(Stack* s);
// 获取栈的大小
int size(Stack* s);
// 实现上述函数的具体内容
// ...
#endif // STACK_H
/* 示例实现部分(通常在单独的.c文件中)
void initStack(Stack* s, int initialCapacity) {
s->elements = (ElementType*)malloc(initialCapacity * sizeof(ElementType));
if (!s->elements) {
// 处理内存分配失败情况...
}
s->capacity = initialCapacity;
s->top = -1; // 初始化时栈为空
}
... // 其他函数的实现
*/
以上定义了栈的抽象数据类型,包括栈的结构体和相关的操作函数原型,但并未给出每个函数的具体实现。在实际项目中,你需要根据所使用的数据结构(顺序栈或链式栈)来完成相应的操作实现,比如扩容、缩容、元素的插入和删除等。
三 栈的顺序存储结构
在C语言中,栈作为一种线性数据结构,其顺序存储结构可以通过动态分配的数组或静态预先分配的数组来实现。以下是一个简单的顺序栈的结构定义及其基本操作实现示例:
// 定义顺序栈结构体
typedef struct {
int *data; // 动态分配的数组,用于存储栈内元素
int capacity; // 栈的容量
int top; // 栈顶指针,指向栈顶元素的位置(数组索引),初始时为-1,表示栈为空
} SqStack;
// 初始化顺序栈
void InitStack(SqStack *s, int cap) {
s->capacity = cap;
s->data = (int *)malloc(cap * sizeof(int)); // 动态分配内存
if (!s->data) {
printf("Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
s->top = -1; // 初始化栈顶指针为-1,表示栈为空
}
// 判断栈是否为空
int IsEmpty(SqStack *s) {
return s->top == -1;
}
// 入栈操作(压栈)
int Push(SqStack *s, int item) {
if (s->top == s->capacity - 1) {
printf("Stack is full!\n");
return FAILURE; // 表示栈满无法入栈
}
s->data[++s->top] = item; // 栈顶指针加1,并将新元素存入
return SUCCESS; // 表示入栈成功
}
// 出栈操作(弹栈)
int Pop(SqStack *s, int *item) {
if (IsEmpty(s)) {
printf("Stack is empty!\n");
return FAILURE; // 表示栈空无法出栈
}
*item = s->data[s->top]; // 取出栈顶元素
s->top--; // 更新栈顶指针
return SUCCESS; // 表示出栈成功
}
// 销毁栈(释放内存)
void DestroyStack(SqStack *s) {
free(s->data);
s->data = NULL;
s->top = -1;
s->capacity = 0;
}
以上实现包括了创建栈、初始化、判断栈是否为空、入栈、出栈和销毁栈的功能。实际应用中,可能还需要其他辅助功能如获取栈顶元素但不移除等。请注意,上述代码没有包含错误处理的完整细节,实际编程时应当确保程序健壮性。同时,为了简化问题,这里假设栈只存储整数类型,若要存储不同类型的元素,可以改为泛型指针或其他灵活的数据结构设计。
三 栈的链式存储结构
在C语言中,栈的链式存储结构通常采用单链表来实现,链栈中的每个节点除了存储数据外,还包含一个指向下一个节点的指针。以下是链栈的基本结构定义以及相应的基本操作实现:
#include <stdio.h>
#include <stdlib.h>
// 定义链栈节点结构体
typedef struct Node {
int data; // 数据域,存储栈中的元素
struct Node *next; // 指针域,指向下一个节点
} StackNode;
// 定义链栈结构体
typedef struct {
StackNode *top; // 栈顶指针,指向栈顶元素
} LinkStack;
// 初始化链栈
void InitStack(LinkStack *s) {
s->top = NULL; // 初始化时栈为空,栈顶指针为空
}
// 判断链栈是否为空
int IsEmpty(LinkStack *s) {
return s->top == NULL;
}
// 创建新节点
StackNode* CreateNode(int data) {
StackNode *node = (StackNode*)malloc(sizeof(StackNode));
if (node == NULL) {
printf("Memory allocation failed.\n");
exit(EXIT_FAILURE);
}
node->data = data;
node->next = NULL;
return node;
}
// 入栈操作(压栈)
void Push(LinkStack *s, int item) {
StackNode *newNode = CreateNode(item);
newNode->next = s->top; // 新节点指向原栈顶
s->top = newNode; // 更新栈顶指针为新节点
}
// 出栈操作(弹栈)
int Pop(LinkStack *s) {
if (IsEmpty(s)) {
printf("Stack is empty, cannot pop.\n");
exit(EXIT_FAILURE); // 或者返回一个错误值
}
StackNode *temp = s->top;
int item = temp->data;
s->top = s->top->next; // 更新栈顶指针为原栈顶的下一个节点
free(temp); // 释放弹出节点的内存
return item; // 返回弹出的栈顶元素
}
// 销毁链栈
void DestroyStack(LinkStack *s) {
while (!IsEmpty(s)) {
StackNode *temp = s->top;
s->top = s->top->next;
free(temp);
}
s->top = NULL; // 清空栈顶指针
}
通过上述代码,我们可以看到链栈相比于顺序栈的优点在于不需要预先确定栈的大小,理论上它可以无限扩展,只要内存允许即可。每次压栈时创建新的节点并将其链接到栈顶,而弹栈时只需修改栈顶指针并将不再需要的节点释放掉。