c语言里栈的类型包括顺序栈和链表栈。
1. 顺序栈
顺序栈中的栈底指针base不会改变指向,改变的是top指针的指向。代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
// 顺序栈
#define MAXSIZE 100
typedef struct stack
{
int *base; // 栈底指针
int *top; // 栈顶指针
int index; // 用int值记录栈顶位置
int stacksize; // 容量
} SqStack;
void InitStack(SqStack *S)
{
S->base = (int *)malloc(MAXSIZE * sizeof(int));
// S->top = (int *)malloc(MAXSIZE * sizeof(int));
S->top = S->base; // 栈顶指向base栈底,表示栈为空
S->index = 0; // 也可用一个int数值判断栈是否为空,0为空
S->stacksize = MAXSIZE;
}
void Push(struct stack *S, int data)
{
if (S->top - S->base == S->stacksize)
{
puts("顺序栈已满");
return;
}
S->top++; // 栈顶指针首地址指向当前地址的下一个地址
*(S->top) = data; // 第一次执行时,访问s.top等价于s.base + 1
}
void Pop(struct stack *S, int *data)
{
if (S->base == S->top)
{
// 栈顶指向栈底
*data = -1;
}
*data = *(S->top);
S->top--; // 栈顶指向上一个地址
}
int getTop(struct stack *S)
{
return *(S->top);
}
void clearStack(struct stack *S)
{
S->base = NULL;
S->top = NULL;
S->index = 0;
S->stacksize = MAXSIZE;
}
char *StackEmpty(struct stack *S)
{
if (S->base == S->top)
{
return "yes"; // 是空栈
}
else
return "no";
}
int StackLength(struct stack *S)
{
return S->top - S->base;
}
void printStack(struct stack *S)
{
int length = StackLength(S);
int i = 1;
while (i <= length)
{
printf("顺序栈的第%d个元素:%d\n", i, *(S->base + i));
i++;
}
}
int main()
{
SqStack node;
InitStack(&node);
Push(&node, 11);
Push(&node, 12);
Push(&node, 13);
Push(&node, 14);
puts("---打印添加后的顺序栈里的元素---");
printStack(&node);
printf("顺序栈的长度:%d\n", StackLength(&node));
puts("---删除顺序栈里的元素---");
int removeData = -1;
Pop(&node, &removeData);
printf("第一次删除的数据:%d\n", removeData);
Pop(&node, &removeData);
printf("第二次删除的数据:%d\n", removeData);
puts("---打印删除后的顺序栈里的元素---");
printStack(&node);
clearStack(&node);
printf("顺序栈是否为空:%s\n", StackEmpty(&node));
return 0;
}
2. 链表栈
链表栈和单链表的定义一致,使用头插法操作栈顶指针。栈顶始终代表头结点,入栈就将新的节点作为头结点,出栈就将栈顶指向头结点的下一个节点来删除头结点:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
// 链表栈头节点始终指向栈顶
#define MAXSIZE 100
typedef struct StackNode
{
int data;
struct StackNode *next;
} StackNode, *LinkStack;
void InitLinkStack(LinkStack *S)
{
*S = NULL;
}
void Push(LinkStack *S, int data)
{
struct StackNode *p = (struct StackNode *)malloc(sizeof(struct StackNode));
p->data = data;
printf("数据%d入栈\n", data);
p->next = *S;
*S = p;
}
int getTop(LinkStack *S)
{
return (*S)->data;
}
void printStack(LinkStack *S)
{
struct StackNode *p = *S;
puts("---从栈顶开始输出内容---");
while (p)
{
printf("%d\n", p->data);
p = p->next;
}
delete p;
}
void Pop(LinkStack *S, int *popData)
{
// struct LinkStack *p = *S;
if(*S == NULL) {
*popData = -1;
} else {
*popData = (*S)->data;
*S = (*S)->next;
puts("栈顶数据被删除");
}
}
int main()
{
LinkStack stack;
InitLinkStack(&stack);
Push(&stack, 11);
Push(&stack, 12);
Push(&stack, 13);
int top = getTop(&stack);
printf("栈顶元素:%d\n", top);
printStack(&stack);
int data = -1;
Pop(&stack, &data);
printf("删除的数据:%d\n", data);
printStack(&stack);
Push(&stack, 14);
printStack(&stack);
return 0;
}
应用场景有表达式求值:
注意当OPTR栈顶元素大于当前字符ch时,会进行计算并且不会读入下一字符,这里其实是多个循环:只要栈顶的运算符优先级较高,那么就一直把这个运算符进行计算,例如1+2*3+5:
当读取到第二个+时,此时OPTR里是[+,*],栈顶是*。OPND里是[1,2,3],栈顶是*。比较第二个+号和*,乘法的优先级更高,这时候OPTR出栈乘法符号*,OPND出栈两个数2和3,计算得6并入栈。此时OPTR里还有[+],OPND里还有[1,6]。由于两个+号之间前面的优先级高,所以OPTR栈顶的+优先级更高,此时继续取出+号和两个数1和6进行运算得到7。此时OPTR为空栈,OPND为[7]。然后第二个+就可以入栈并读取下一个字符5了。下一次计算的就是7+5,这也符号数学里的运算逻辑。