栈和队列:理解与使用

目录

顺序栈结构

链式栈结构

中缀表达式和后缀表达式

顺序栈四则运算

链式栈四则运算

浮点数四则运算

队列

顺序队列结构

链式队列结构

总结


栈和队列是计算机科学中常见的数据结构,它们都是一种线性数据结构,可以对元素进行快速的插入、删除和查找操作。栈和队列都可以用于各种不同的应用场景,不过它们的使用方式和特点有所不同。

首先,让我们来了解一下栈的概念。栈是一种具有“后进先出”(Last In First Out,LIFO)特性的数据结构,如图:

只有栈顶元素是可以访问的。新加入的元素会直接放在栈顶,而每次需要访问栈元素时,都会从栈顶开始弹出元素。栈常用于实现函数调用、表达式求值、括号匹配等场景。通常使用 push() 方法将一个元素压入栈中,使用 pop() 方法将栈顶元素弹出。

栈结构包括两类:顺序栈结构和链式栈结构。

顺序栈结构

顺序栈结构使用一组地址连续的内存单元依次保存栈中的数据。在程序中,可以定义一个指定大小的结构数组作为栈,定义一个变量top保存栈顶序号,初始为-1表示空栈。顺序栈的定义和常见操作及代码如下:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    char *data;
    int top;
    int maxSize;
} Stack;

// 初始化栈
void initStack(Stack *stack, int maxSize) {
    stack->data = (char *)malloc(sizeof(char) * maxSize);
    stack->top = -1;
    stack->maxSize = maxSize;
}

// 判断栈是否为空
int isEmpty(Stack *stack) {
    return stack->top == -1;
}

// 判断栈是否已满
int isFull(Stack *stack) {
    return stack->top == stack->maxSize;
}

// 入栈
void push(Stack *stack, char element) {
    if (isFull(stack)) {
        printf("栈已满,无法入栈!\n");
        return;
    }
    stack->data[++stack->top] = element;
}

// 出栈
char pop(Stack *stack) {
    if (isEmpty(stack)) {
        printf("栈已空,无法出栈!\n");
        return 0;
    }
    return stack->data[stack->top--];
}

// 读取栈顶元素
char peek(Stack *stack) {
    if (isEmpty(stack)) {
        printf("栈已空,无法读取栈顶元素!\n");
        return 0;
    }
    return stack->data[stack->top];
}

// 清空栈
void clear(Stack *stack) {
    stack->top = -1;
}

// 销毁栈
void destroy(Stack *stack) {
    free(stack->data);
    stack->data = NULL;
    stack->top = -1;
    stack->maxSize = 0;
}

链式栈结构

链式栈结构使用链表保存栈元素值,链表头部为栈顶,链表尾部为栈底。链式栈的定义和常见操作及代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DATA_SIZE 20

// 链式栈结点结构
typedef struct Node {
    char data[DATA_SIZE];
    struct Node *next;
} Node, *LinkedStack;

// 初始化栈
void initStack(LinkedStack *top) {
    *top = NULL;
}

// 判断栈是否为空
int isEmpty(LinkedStack *top) {
    return *top == NULL;
}

// 入栈
void push(LinkedStack *top, char *data) {
    //printf("push: %s ", data);
    Node *node = (Node *)malloc(sizeof(Node));
    strcpy(node->data, data);
    node->next = *top;
    *top = node;
}

// 出栈
void pop(LinkedStack *top, char *data) {
    if (isEmpty(top)) {
        printf("栈已空,无法出栈!\n");
        return;
    }
    Node *node = *top;
    *top = node->next;
    strcpy(data, node->data);
    //printf("pop: %s ", data);
    free(node);
}

// 读取栈顶元素
char* peek(LinkedStack *top) {
    if (isEmpty(top)) {
        printf("栈已空,无法读取栈顶元素!\n");
        return NULL;
    }
    return (*top)->data;
}

// 清空栈
void clear(LinkedStack *top) {
    while (!isEmpty(top)) {
        Node *node = *top;
        *top = node->next;
        free(node);
    }
}

// 销毁栈
void destroy(LinkedStack *top) {
    clear(top);
}

// 打印栈
void print(LinkedStack *top) {
    if (isEmpty(top)) {
        printf("栈已空,无法打印!\n");
        return;
    }
    printf("栈中元素:");
    Node *node = *top;
    while (node != NULL) {
        printf("%s ", node->data);
        node = node->next;
    }
    printf("\n");
}

中缀表达式和后缀表达式

前面提到栈可以用于实现表达式求值,在展示这个案例前,我们先了解一下表达式求值的应用过程。我们平时使用的表达式如“2*(5-1)”是中缀表达式,使用括号来明确运算符的优先级和结合性。后缀表达式(也称为逆波兰表达式),是一种将运算符放置在操作数之后的表示方法,将前面的中缀表达式转换为后缀表达式就是“251-*”。将中缀表达式转换为后缀表达式,可以使表达式更容易计算,而不需要考虑括号和运算符优先级。将中缀表达式 "2*(5-1)" 转换为后缀表达式的过程如下:

  1.  从左到右遍历中缀表达式 "2*(5-1)";
  2. 遇到操作数 2,将其添加到后缀表达式列表中;
  3. 遇到运算符 *,将其入栈;
  4. 遇到左括号 (,将其入栈;
  5. 遇到操作数 5,将其添加到后缀表达式列表中;
  6. 遇到运算符 -,与栈顶运算符 * 比较,栈顶优先级较低,将 - 入栈(如果栈顶运算符优先级高,则弹出并添加到后缀表达式列表中)。
  7. 遇到操作数 1,将其添加到后缀表达式列表中;
  8. 遇到右括号 ),将栈顶的运算符 - * 依次弹出并添加到后缀表达式列表中,直到遇到左括号 ( 时停止,左括号丢弃;
  9. 中缀表达式遍历完成后,如果栈中还有剩余运算符,则将栈中剩余的运算符依次弹出并添加到后缀表达式列表中。

处理后缀表达式求值的过程则相对简单,因为不需要考虑括号和运算符优先级:

  1. 从左到右遍历后缀表达式中的每个元素;
  2. 如果遇到操作数,将其压入栈中;
  3. 如果遇到运算符,从栈中弹出 2 个操作数,根据运算符进行计算,并将结果压入栈中;
  4. 重复步骤 2 和步骤 3,直到遍历完整个后缀表达式。
  5. 最后,栈中将只剩下一个元素&#
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创意程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值