栈

另类堆栈实现解析:Top 指针定义为栈顶的上一个位置
一、问题背景
在传统的堆栈实现中,栈顶指针Top通常指向栈顶元素所在的位置。然而,在某些应用场景中,我们会遇到一种 "另类" 的堆栈定义:将Top定义为栈顶元素的上一个位置。这种定义方式在判断堆栈空满状态以及元素操作时有其独特之处。本文将详细解析这种另类堆栈的实现方法。
二、堆栈结构定义
首先,让我们看看题目中给出的堆栈结构定义:
c
typedef int Position;
typedef struct SNode *PtrToSNode;
struct SNode {
ElementType Data; / 存储元素的数组 /
Position Top; /
栈顶指针 /
int MaxSize; /
堆栈最大容量 */
};
typedef PtrToSNode Stack;
这里的关键在于Top指针的定义。在这种另类堆栈中,Top指向的是栈顶元素的上一个位置,而不是栈顶元素本身。这意味着当堆栈为空时,Top的值为 0;随着元素入栈,Top的值会递增,指向数组中下一个可用位置。
三、入栈操作实现
入栈操作需要考虑堆栈已满的情况,并正确更新Top指针:
c
bool Push( Stack S, ElementType X ) {
if ( S->Top == S->MaxSize ) {
printf("Stack Full\n");
return false;
}
S->Data[S->Top] = X;
S->Top++;
return true;
}
代码解析:
判断堆栈已满:当Top等于MaxSize时,表示堆栈已满。因为Top指向的是下一个可用位置,当它等于数组大小时,说明数组已经没有空间了。
元素入栈:将元素存入Data[Top]位置,然后将Top加 1,指向下一个可用位置。
四、出栈操作实现
出栈操作需要考虑堆栈为空的情况,并正确更新Top指针:
c
ElementType Pop( Stack S ) {
if ( S->Top == 0 ) {
printf("Stack Empty\n");
return ERROR;
}
S->Top--;
return S->Data[S->Top];
}
代码解析:
判断堆栈为空:当Top等于 0 时,表示堆栈为空。因为Top初始值为 0,且只有入栈操作会增加Top的值,所以当Top为 0 时,说明堆栈中没有元素。
元素出栈:先将Top减 1,使其指向栈顶元素,然后返回该元素。
五、判断堆栈空满状态
基于这种Top指针的定义,判断堆栈空满状态的条件如下:
堆栈为空:当且仅当Top == 0时,堆栈为空。
堆栈已满:当且仅当Top == MaxSize时,堆栈已满。
这种判断方式与传统堆栈(Top指向栈顶元素)有所不同,需要特别注意。
六、完整实现与测试
下面是完整的实现代码,包括入栈、出栈操作以及主函数测试部分:
c

include <stdio.h>

include <stdlib.h>

define ERROR -1

typedef int ElementType;
typedef enum { push, pop, end } Operation;
typedef enum { false, true } bool;
typedef int Position;
typedef struct SNode *PtrToSNode;
struct SNode {
ElementType Data; / 存储元素的数组 /
Position Top; /
栈顶指针 /
int MaxSize; /
堆栈最大容量 */
};
typedef PtrToSNode Stack;

Stack CreateStack( int MaxSize )
{
Stack S = (Stack)malloc(sizeof(struct SNode));
S->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType));
S->Top = 0;
S->MaxSize = MaxSize;
return S;
}

bool Push( Stack S, ElementType X ) {
if ( S->Top == S->MaxSize ) {
printf("Stack Full\n");
return false;
}
S->Data[S->Top] = X;
S->Top++;
return true;
}

ElementType Pop( Stack S ) {
if ( S->Top == 0 ) {
printf("Stack Empty\n");
return ERROR;
}
S->Top--;
return S->Data[S->Top];
}

/* 以下为测试代码,题目中已提供 /
Operation GetOp(); /
裁判实现,细节不表 /
void PrintStack( Stack S ); /
裁判实现,细节不表 */

int main()
{
ElementType X;
Stack S;
int N, done = 0;

scanf("%d", &N);
S = CreateStack(N);
while ( !done ) {
switch( GetOp() ) {
case push:
scanf("%d", &X);
Push(S, X);
break;
case pop:
X = Pop(S);
if ( X!=ERROR ) printf("%d is out\n", X);
break;
case end:
PrintStack(S);
done = 1;
break;
}
}
return 0;
}
七、测试样例分析
输入样例:
plaintext
4
Pop
Push 5
Push 4
Push 3
Pop
Pop
Push 2
Push 1
Push 0
Push 10
End
输出样例:
plaintext
Stack Empty
3 is out
4 is out
Stack Full
0 1 2 5
执行过程分析:
Pop 操作:初始时堆栈为空,输出 "Stack Empty"。
Push 5, 4, 3:依次将 5、4、3 入栈,此时栈内元素为 [5, 4, 3],Top=3。
Pop, Pop:连续两次出栈,依次输出 3 和 4,此时栈内元素为 [5],Top=1。
Push 2, 1, 0:依次将 2、1、0 入栈,此时栈内元素为 [5, 2, 1, 0],Top=4。
Push 10:堆栈已满(Top=4 等于 MaxSize),输出 "Stack Full"。
End:打印栈内元素,顺序为 0、1、2、5。
八、总结
这种将Top定义为栈顶元素上一个位置的堆栈实现方式,虽然与传统方式不同,但通过合理设计入栈和出栈操作,同样可以实现堆栈的基本功能。理解这种另类实现方式,有助于加深对堆栈数据结构的理解,以及在不同场景下的灵活应用。
通过本文的解析,相信读者已经掌握了这种特殊堆栈定义的实现方法和关键点。在实际编程中,根据具体需求选择合适的实现方式,可以更高效地解决问题。
中缀表达式转后缀表达式:原理与实现
一、什么是后缀表达式?
在计算机科学中,表达式有三种表示方法:
前缀表达式(波兰式):运算符在前,操作数在后,如+ 3 4
中缀表达式:运算符在两个操作数中间,如3 + 4(人类最习惯的写法)
后缀表达式(逆波兰式):运算符紧跟在操作数后,如3 4 +
后缀表达式的优势在于不需要括号来指定运算顺序,计算机可以更高效地处理。例如:
中缀表达式:(3+4)*5
后缀表达式:3 4 + 5 *
二、转换原理:为什么需要栈?
将中缀表达式转换为后缀表达式的核心思想是利用栈来控制运算符的优先级。具体规则如下:
操作数:直接输出
左括号:压入栈中
右括号:弹出栈中元素直到遇到左括号
运算符:比较其与栈顶运算符的优先级
如果当前运算符优先级高于栈顶,则压入栈
否则弹出栈顶元素直到满足条件,再压入当前运算符
三、代码实现解析
下面是中缀转后缀的 C++ 实现代码:
cpp

include <bits/stdc++.h>

using namespace std;

void to_deal(string str) {
stack stk;
// 定义运算符优先级
map<char, int> m;
m['+'] = m['-'] = 1;
m['*'] = m['/'] = 2;
m['('] = m[')'] = 3;

int index = 0;
bool flag = true; // 判断是否是第一个输出

while (index < str.size()) {
// 处理操作数(包括带符号的数)
if ((index < 1 || str[index - 1] == '(') &&
(str[index] == '+' || str[index] == '-') ||
isdigit(str[index])) {
if (flag) flag = false;
else printf(" ");

if (str[index] != '+') printf("%c", str[index]);
while (str[index + 1] == '.' || isdigit(str[index + 1]))
printf("%c", str[++index]);
index++;
} else {
// 处理运算符
if (str[index] == '(') stk.push(str[index]);
else if (str[index] == ')') {
// 弹出直到遇到左括号
while (!stk.empty() && stk.top() != '(') {
printf(" %c", stk.top());
stk.pop();
}
stk.pop(); // 弹出左括号
} else {
// 比较优先级,弹出优先级大于等于当前的运算符
while (!stk.empty() && stk.top() != '(' &&
m[stk.top()] >= m[str[index]]) {
printf(" %c", stk.top());
stk.pop();
}
stk.push(str[index]);
}
index++;
}
}

// 输出栈中剩余的运算符
while (!stk.empty()) {
printf(" %c", stk.top());
stk.pop();
}
}
代码关键点分析:
优先级定义:
cpp
m['+'] = m['-'] = 1;
m['*'] = m['/'] = 2;
m['('] = m[')'] = 3;

*和/的优先级高于+和-
括号单独处理,左括号压栈,右括号触发弹出操作
操作数处理:
cpp
if ((index < 1 || str[index - 1] == '(') &&
(str[index] == '+' || str[index] == '-') ||
isdigit(str[index]))

处理带符号的操作数(如-5或+3)
处理多位数字和小数(如3.14)
运算符处理逻辑:
左括号直接压栈
右括号弹出栈中元素直到遇到左括号
其他运算符比较优先级,确保高优先级的运算符先输出
四、示例运行过程
以输入2+3*(7-4)+8/4为例,转换过程如下:
步骤 输入字符 栈状态 输出
1 2 空 2
2 + + 2
3 3 + 2 3
4 * + * 2 3
5 ( + * ( 2 3
6 7 + * ( 2 3 7
7 - + * ( - 2 3 7
8 4 + * ( - 2 3 7 4
9 ) + * 2 3 7 4 -
10 + + 2 3 7 4 - * +
11 8 + 2 3 7 4 - * + 8
12 / + / 2 3 7 4 - * + 8
13 4 + / 2 3 7 4 - * + 8 4
14 结束 空 2 3 7 4 - * + 8 4 / +
最终输出后缀表达式:2 3 7 4 - * + 8 4 / +
五、复杂度分析
时间复杂度:O (n),每个字符仅处理一次

posted @ 2025-05-22 20:24  林宇钦  阅读(109)  评论(0)    收藏  举报