用C实现栈(链表|数组)

133 阅读3分钟

栈的定义

栈的实现(链表)

通用头文件 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语言描述》