2 栈和队列(Stack and Queue)
2.2 链栈(Linked Stack)
链栈是指采用链式存储结构实现的栈。通常链栈用单链表来表示。

基础代码:
// 声明一些常量
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define TRUE 1
#define FALSE 0
// Status 是函数返回值类型, 其值是函数结果状态代码。
typedef int Status;
// Boolean 定义布尔型,值就是 TRUE 和 FALSE。
typedef int Boolean;
栈元素结构和栈结构:
// 栈元素类型定义
typedef struct
{
int data; // 栈元素的数据
} SElemType;
// 链栈的存储结构
typedef struct StackNode
{
SElemType data;
struct StackNode *next;
} StackNode, *LinkStack;
在编写代码过程中,经常需要查看栈内的内容,因此需要一个方法可以遍历栈,并打印其中的内容:
// 遍历链栈
void TraverseStack(LinkStack S)
{
StackNode *current = S;
while (current != NULL)
{
printf("%d ", current->data.data); // 打印栈元素数据
current = current->next; // 移动到下一个结点
}
printf("\n");
}
2.2.1 初始化
链栈的初始化操作就是构造一个空栈, 因为没必要设头结点, 所以直接将栈顶指针置空即可。
// 初始化链栈
Status InitStack(LinkStack *S)
{
*S = NULL; // 初始化链栈为空
return OK;
}
2.2.2 入栈
和顺序栈的入栈操作不同的是, 链栈在入栈前不需要判断栈是否满,只需要为入栈元素动态分配一个结点空间。

【算法步骤】
- 为入栈元素e分配空间,用指针newNode指问。
- 将新结点数据域置为 e。
- 将新结点插人栈顶。
- 修改栈顶指针为 newNode。
【代码实现】
// 入栈操作
Status Push(LinkStack *S, SElemType e)
{
StackNode *newNode = (StackNode *)malloc(sizeof(StackNode));
if (!newNode) // 分配失败
return OVERFLOW;
newNode->data = e; // 设置新结点的数据
newNode->next = *S; // 新结点的next指针指向当前栈顶
*S = newNode; // 更新栈顶指针
return OK;
}
2.2.3 出栈
和顺序栈一样, 链栈在出栈前也需要判断栈是否为空, 不同的是, 链栈在出栈后需要释放出栈元素的栈顶空间。

【算法步骤】
- 判断栈是否为空,若空则返回 ERROR。
- 将栈顶元素赋给 e。
- 临时保存栈顶元素的空间,以备释放。
- 修改栈顶指针,指向新的栈顶元素。
- 释放原栈顶元素的空间。
【代码实现】
// 出栈操作
Status Pop(LinkStack *S, SElemType *e)
{
if (*S == NULL) // 栈空
return ERROR;
*e = (*S)->data; // 将栈顶元素赋值给 e
StackNode *temp = *S; // 临时指针保存当前栈顶结点
*S = temp->next; // 更新栈顶指针
free(temp); // 释放原栈顶结点内存
return OK;
}
2.2.4 获取栈顶元素
与顺序栈一样, 当栈非空时, 此操作返回当前栈顶元素的值, 栈顶指针S保持不变。
// 获取栈顶元素
Status GetTop(LinkStack S, SElemType *e)
{
if (S == NULL) // 栈空
return ERROR;
*e = S->data; // 获取栈顶元素
return OK;
}
2.2.5 销毁栈
因为栈内的元素是动态申请的,所以销毁栈需要遍历每一个栈内元素,释放其内存。
// 销毁链栈
Status DestroyStack(LinkStack *S)
{
StackNode *current = *S;
StackNode *temp;
while (current != NULL)
{
temp = current; // 保存当前结点
current = current->next; // 移动到下一个结点
free(temp); // 释放当前结点内存
}
*S = NULL; // 将栈顶指针置为 NULL
return OK;
}
2.2.6 判断栈是否为空
// 判断链栈是否为空
Boolean IsEmpty(LinkStack S)
{
return S == NULL; // 如果栈顶指针为 NULL,则栈为空
}
2.2.7 获取栈的大小
和顺序栈固定大小不同,链栈需要遍历每一个元素,然后进行累加。
// 获取链栈的大小
int StackSize(LinkStack S)
{
int size = 0;
StackNode *current = S;
while (current != NULL)
{
size++; // 计数器加一
current = current->next; // 移动到下一个结点
}
return size; // 返回栈的大小
}
时间复杂度是 O(n)。
2.2.8 清空栈操作
清空链栈需要遍历栈内每一个元素,然后释放其内存,最后将头指针置为NULL。从代码上看和销毁栈的操作是一样的,但从业务含义上看是不一样的。
// 清空链栈
Status ClearStack(LinkStack *S)
{
StackNode *current = *S;
StackNode *temp;
while (current != NULL)
{
temp = current; // 保存当前结点
current = current->next; // 移动到下一个结点
free(temp); // 释放当前结点内存
}
*S = NULL; // 将栈顶指针置为 NULL
return OK;
}
5271

被折叠的 条评论
为什么被折叠?



