栈的定义
栈的实现(链表)
通用头文件 fatal.h
#include <stdio.h>
#include <stdlib.h>
#define Error( Str ) FatalError( Str )
#define FatalError( Str ) fprintf( stderr, "%s\n", Str ), exit( 1 )
头文件 stackli.h
typedef int ElementType;
#ifndef _Stack_h
#define _Stack_h
struct Node;
typedef struct Node *PtrToNode;
typedef PtrToNode Stack;
int IsEmpty(Stack S);
Stack CreateStack(void);
void DisposeStack(Stack S);
void MakeEmpty(Stack S);
void Push(ElementType X, Stack S);
ElementType Top(Stack S);
void Pop(Stack S);
#endif
实现代码 stackli.c
#include "stackli.h"
#include "fatal.h"
#include <stdlib.h>
struct Node
{
ElementType Element;
PtrToNode Next;
};
int
IsEmpty(Stack S)
{
return S->Next == NULL;
}
Stack
CreateStack(void)
{
Stack S;
S = malloc(sizeof(struct Node));
if (S == NULL)
FatalError("Out of space!!!");
S->Next = NULL;
// 确保S指向的地址确实是一个空栈
MakeEmpty(S);
return S;
}
void
MakeEmpty(Stack S)
{
if (S == NULL)
Error("Must use CreateStack first");
else
while (!IsEmpty(S))
Pop(S);
}
void
DisposeStack(Stack S)
{
MakeEmpty(S);
// 释放S指针指向的内存区域
free(S);
}
void
Push(ElementType X, Stack S)
{
PtrToNode TmpCell;
TmpCell = malloc(sizeof(struct Node));
if (TmpCell == NULL)
FatalError("Out of space!!!");
else
{
// 在头部节点插入新的节点
TmpCell->Element = X;
TmpCell->Next = S->Next;
S->Next = TmpCell;
}
}
ElementType
Top(Stack S)
{
// 取值也是取头部节点
if (!IsEmpty(S))
return S->Next->Element;
Error("Empty stack");
return 0;
}
void
Pop(Stack S)
{
PtrToNode FirstCell;
if (IsEmpty(S))
Error("Empty stack");
else
{
// 把 S->Next 指向原来第一个节点的后面一个
FirstCell = S->Next;
S->Next = S->Next->Next;
free(FirstCell);
}
}
测试代码 teststkl.c
#include <stdio.h>
#include "stackli.h"
main()
{
Stack S;
int i;
S = CreateStack();
for (i = 0; i < 10; i++)
Push(i, S);
while (!IsEmpty(S))
{
printf("%d\n", Top(S));
Pop(S);
}
DisposeStack(S);
return 0;
}
输出如下
9
8
7
6
5
4
3
2
1
0
栈的实现(数组)
头文件 stackar.h
typedef int ElementType;
#ifndef _Stack_h
#define _Stack_h
struct StackRecord;
typedef struct StackRecord *Stack;
int IsEmpty(Stack S);
int IsFull(Stack S);
Stack CreateStack(int MaxElements);
void DisposeStack(Stack S);
void MakeEmpty(Stack S);
void Push(ElementType X, Stack S);
ElementType Top(Stack S);
void Pop(Stack S);
ElementType TopAndPop(Stack S);
#endif
实现 stackar.c
#include "stackar.h"
#include "fatal.h"
#include <stdlib.h>
#define EmptyTOS (-1)
#define MinStackSize (5)
struct StackRecord
{
int Capacity; // 数组容量
int TopOfStack; // 指向栈的顶部位置
ElementType *Array; // 存放数据的数组
};
int
IsEmpty(Stack S)
{
// 栈顶位置-1
return S->TopOfStack == EmptyTOS;
}
int
IsFull(Stack S)
{
// 栈顶位置到达数组尾部
return S->TopOfStack == S->Capacity - 1;
}
Stack
CreateStack(int MaxElements)
{
Stack S;
if (MaxElements < MinStackSize)
Error("Stack size is too small");
S = malloc(sizeof(struct StackRecord));
if (S == NULL)
FatalError("Out of space!!!");
S->Array = malloc(sizeof(ElementType) * MaxElements);
if (S->Array == NULL)
FatalError("Out of space!!!");
S->Capacity = MaxElements;
MakeEmpty(S);
return S;
}
void
MakeEmpty(Stack S)
{
// 通过移动栈顶位置来控制 数组内部本身不用关心
S->TopOfStack = EmptyTOS;
}
void
DisposeStack(Stack S)
{
if (S != NULL)
{
free(S->Array);
free(S);
}
}
void
Push(ElementType X, Stack S)
{
if (IsFull(S))
Error("Full stack");
else
// 先自增栈顶位置 然后将该位置的数组元素复制X
S->Array[++S->TopOfStack] = X;
}
ElementType
Top(Stack S)
{
if (!IsEmpty(S))
return S->Array[S->TopOfStack];
Error("Empty stack");
return 0; /* Return value used to avoid warning */
}
void
Pop(Stack S)
{
if (IsEmpty(S))
Error("Empty stack");
else
// 栈顶位置自减
S->TopOfStack--;
}
ElementType
TopAndPop(Stack S)
{
// 先取出栈顶位置的值 然后再将它自减
if (!IsEmpty(S))
return S->Array[S->TopOfStack--];
Error("Empty stack");
return 0; /* Return value used to avoid warning */
}
测试 teststka.c
#include <stdio.h>
#include "stackar.h"
main()
{
Stack S;
int i;
S = CreateStack(12);
for (i = 0; i < 10; i++)
Push(i, S);
while (!IsEmpty(S))
{
printf("%d\n", Top(S));
Pop(S);
}
DisposeStack(S);
return 0;
}
输出
9
8
7
6
5
4
3
2
1
0
实战
leetcode 224题
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。
示例 1:
输入: "1 + 1" 输出: 2
示例 2:
输入: " 2-1 + 2 " 输出: 3
示例 3:
输入: "(1+(4+5+2)-3)+(6+8)" 输出: 23
说明:
你可以假设所给定的表达式都是有效的。 请不要使用内置的库函数 eval。
typedef long int ElementType;
struct Listnode {
ElementType data;
struct Listnode *Next;
};
typedef struct Listnode Node;
typedef Node *Stack;
Stack genStack()
{
Stack S;
S = malloc(sizeof(Node));
S->Next = NULL;
return S;
}
void push(ElementType x, Stack S)
{
Node *p;
p = malloc(sizeof(Node));
p->data = x;
p->Next = S->Next ? S->Next : NULL;
S->Next = p;
}
ElementType pop(Stack S)
{
ElementType x = -1;
if (S->Next) {
Node *P = S->Next;
S->Next = S->Next->Next;
x = P->data;
free(P);
}
return x;
}
ElementType top(Stack S)
{
return S->Next ? S->Next->data : -1;
}
ElementType calculate(char * s){
ElementType answer = 0;
Stack OptS = genStack();
Stack NumS = genStack();
char *P = s;
while(*P != '\0')
{
if (*P == ' ' || *P == '"' )
{
++P;
continue;
}
else if (*P == '(')
{
push((ElementType)(*P), OptS);
}
else if (*P == ')')
{
while(((char)top(OptS)) != '(')
{
char preOpt = (char)pop(OptS);
if (preOpt == '+') {
push(pop(NumS) + pop(NumS), NumS);
}
else if (preOpt == '-')
{
ElementType preVal = pop(NumS);
push(pop(NumS) - preVal, NumS);
}
}
pop(OptS);
}
else if (*P == '+' || *P == '-')
{
if (top(OptS) == -1 || (char)top(OptS) == '(')
{
push((ElementType)*P, OptS);
}
else
{
char preOpt = (char)pop(OptS);
if (preOpt == '+') {
push(pop(NumS) + pop(NumS), NumS);
}
else if (preOpt == '-')
{
ElementType preVal = pop(NumS);
push(pop(NumS) - preVal, NumS);
}
push((ElementType)*P, OptS);
}
}
else
{
// 数字字符 可能有多位
ElementType num = *P - '0';
++P;
while((*P - '0') >= 0 && (*P - '0') <= 9) {
// 连续数字字符
num = 10*num + *P - '0';
++P;
}
push(num, NumS);
// 回退一位 因为多确认了一个字符
--P;
}
++P;
}
if (top(OptS) != -1) {
// 操作符栈非空
char preOpt = (char)pop(OptS);
if (preOpt == '+') {
push(pop(NumS) + pop(NumS), NumS);
}
else if (preOpt == '-')
{
ElementType preVal = pop(NumS);
push(pop(NumS) - preVal, NumS);
}
}
answer = (ElementType)pop(NumS);
return answer;
}
这里我们用的链表的形式实现栈的,在代码中因为多次调用 malloc 导致运行时间较慢,可以改造成数组形式实现的栈,时间上会有明显的提升
参考: 《数据结构与算法分析: C语言描述》