栈
1.栈的概念
栈(stack)又名堆栈,它是一种运算受限的线性表。栈(stack)是限制在表的一端进行插入和删除运算的线性表,通常称插入、删除的这一端为栈顶(top),另一端为栈底(bottom)。当表中没有元素时称为空栈。
向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
假设栈S=(a1,a2,a3,…an),则a1称为栈底元素,an为栈顶元素。栈中元素按a1,a2,a3,…an的次序进栈,退栈的第一个元素应为栈顶元素。换句话说,栈的修改是按后进先出的原则进行的。因此,栈称为后进先出表。
例:
对于一个栈,给出输入项A、B、C,如果输入项序列由ABC组成,试给出所有可能的输出序列。
2.顺序栈
2.1顺序栈的概念
栈的顺序存储结构简称为顺序栈,它是运算受限的顺序表。因此,可用数组来实现顺序栈。
因为栈底位置是固定不变的,所以可以将栈底位置设置在数组的两端的任何一个端点;栈顶位置是随着进栈和退栈操作而变化的,故需用一个整型变量top 来指示当前栈顶的位置,通常称 top 为栈顶指针。因此,顺序栈的类型定义只需将顺序表的类型定义中的长度属性改为 top 即可。
2.2 顺序栈的类型定义
# define stacksize 100
typedef char datatype;
typedef struct {
datatype data[stacksize];
int top;
}seqstack;
设 s 是 seqstack 类型的指针变量。若栈底位置在向量的低端,即 s–>data[0] 是栈底元素,那么栈顶指针 s–>top 是正向增加的,即进栈时需将 s–>top 加1,退栈时需将 s–>top 减1。因此,s–>top<0 表示空栈, s–>top = stacksize-1 表示栈满。当栈满时再做进栈运算必定产生空间溢出,简称“上溢”;当栈空时再做退栈运算也将产生溢出,简称“下溢”。上溢是一种出错状态,应该设法避免之;下溢则可能是正常现象,因为栈在程序中使用时,其初态或终态都是空栈,所以下溢常常用来作为程序控制转移的条件。
2.3顺序栈的基本运算
置空栈
void initstack(seqstack *s)
{
s–>top=-1;
}
判断栈空
int stackempty(seqstack *s)
{
return(s–>top==-1);
}
判断栈满
int stackfull(seqstack *s)
{
return(s–>top==stacksize-1);
}
进栈
void push(seqstack *s,datatype x)
{
if(stackfull(s))error(“stack overflow”);
s–>data[++s–>top]=x;
}
出栈
datatype pop(seqstack *s)
{
if(stackempty(s))
error(“stack underflow”);
x=s–>data[top];
s–>top--;
return(x);
}
取栈顶元素
datatype stacktop(seqstack *s)
{
if(stackempty(s)
error(“stack is empty”);
return s–>data[s–>top];
}
3.链栈
3.1链栈的概念
栈的链式存储结构称为链栈,它的运算是受限的单链表,插入和删除操作仅限制在表头位置上进行。由于只能在链表头部进行操作,故链表没有必要像单链表那样附加头结点。栈顶指针就是链表的头指针。
3.2链栈的特点
- 链式栈无栈满问题,空间可扩充
- 插入与删除仅在栈顶处执行
- 链式栈的栈顶在链头
- 适合于多栈操作(可实现两个堆栈共享空间)
3.3链栈的类型定义
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
}linkstack;
3.4链栈的基本运算
进栈
linkstack *PUSHLSTACK(linkstack *top, datatype x)
{
linkstack *p;
p=malloc(sizeof(linkstack));
p->data=x; p->next=top;
return p;
}
出栈
linkstack *POPLSTACK(linkstack *top, datatype datap)
{
linkstack *p;
if(top==NULL)
{
printf("under flow\n");
return NULL;
}
else
{
*datap=top->data;
p=top;
top=top->next;
free(p);
return top;
}
}
4.栈的应用
4.1数制转换
十进制N和其它进制数的转换是计算机实现计算的基本问题,其解决方法很多,其中一个简单算法基于下列原理:
N=(n div d)*d+(n mod d)
( 其中:div为整除运算,mod为求余运算)
例如 (1348)10=(2504)8,其运算过程如下:
n | n div 8 | n mod 8 |
---|---|---|
1348 | 168 | 4 |
168 | 21 | 0 |
21 | 2 | 5 |
2 | 0 | 2 |
C语言实现:
void conversion()
{
initstack(s);
scanf ("%",&n);
while(n)
{
push(s,n%8);
n=n/8;
}
while(! stackempty(s))
{
pop(s,e);
printf("%d",e);
}
}
4.2文字编辑器
输入的一个字符是错的之后,可以再输入一个 ‘#’ ,表示前一个字符是错的,如果发现当前行输入的错误太多,可以输入一个退行符 ‘@’ ,表示当前行都无效。
seqstack s;
int edit()
{
char c;
setnull(&s);
c=getchar();
while(c!='*')
{
if (c=='#')pop(&s);
else if(c=='@')sttnull(&s);
else push(&s,c);
c=getchar();
}
}
4.3表达式计算
中缀表达式:A+(B-C/D)×E
后缀表达式:ABCD/-E×+
后缀表达式特点
- 1、与相应的中缀表达式中的操作数次序相同
- 2、没有括号
处理规则
- 1、如为操作数,直接输出到队列;
- 2、如当前运算符高于栈顶运算符,入栈;
- 3、如当前运算符低于栈顶运算符,栈顶运算符退栈, 并输出到队列,当前运算符再与栈顶运算符比较;
- 4、如当前运算符等于栈顶运算符,且栈顶运算符为 “(”,当前运算符为“)”,则栈顶运算符退栈, 继续读下一符号;
- 5、如当前运算符等于栈顶运算符,且栈顶运算符为 “#”,当前运算符也为“#”,则栈顶运算符退栈,继续读下一符号;
步骤
步骤 | 中缀表达式 | stack | 输出 |
---|---|---|---|
1 | A+(B-C/D)×E# | # | |
2 | +(B-C/D)×E# | # | A |
3 | (B-C/D)×E# | # + | A |
4 | B-C/D)×E# | # +( | A |
5 | -C/D)×E# | # + ( | A B |
6 | C/D)×E# | # + ( - | AB |
7 | /D)×E# | # + ( - | ABC |
8 | D)×E# | # + ( - / | ABC |
9 | )×E# | # + ( - / | ABCD |
10 | ×E# | # + ( - | ABCD/ |
11 | ×E# | # + ( | ABCD/- |
12 | ×E# | # + | ABCD/- |
13 | E# | # + × | ABCD/- |
14 | # | # + × | ABCD/-E |
15 | # | # + | ABCD/-E× |
16 | # | # | ABCD/-E×+ |
文章内容来源于博主老师传授、自身理解以及网络收集