顺序栈
利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。
① 一组地址连续的存储单元:数组;
② 附设一个栈顶指针top来动态地指示栈顶元素在顺序栈中的位置。通常以top = -1表示空栈。
进栈与出栈:
0、存储结构:
#define Stack_Size 50
typedef struct
{
StackElementType elem[Stack_Size]; /*元素数组*/
int top; /*栈顶下标*/
}SeqStack;
1、初始化:
void InitStack(SeqStack *S)
{
S->top = -1;
}
2、判栈空:
int IsEmpty(SeqStack *S)
{
return(S->top == -1);
}
3、判栈满:
int IsFull(SeqStack *S)
{
return(S->top == Stack_Size - 1);
}
4、进栈:
int Push(SeqStack * S, StackElementType x)
{
if(S->top == Stack_Size - 1)
return(FALSE); /*栈满*/
S->top++;
S->elem[S->top] = x;
return(TRUE);
}
5、出栈:
int Pop(SeqStack * S, StackElementType *x)
{
if(S->top == -1)
return(FALSE); /*栈空*/
else
{
*x = S->elem[S->top];
S->top--; /* 修改栈顶指针 */
return(TRUE);
}
}
6、取栈顶元素
int GetTop(SeqStack S, StackElementType *x)/* 取栈顶元素到x,栈顶不变 */
{
if(S->top == -1)
return(FALSE); /*栈为空*/
else
{
*x = S->elem[S->top];
return(TRUE);
}
}
两栈共享技术
同时使用多栈,分别分配空间会造成有些不够用,有些空闲的情况,可以多栈共享空间。
为两个栈申请一个共享的一维数组空间S[M]——双端栈
① 数组两端(0,M-1)
分别作为两栈栈底;
② top[0]、top[1]
分别为两栈顶指示器(两栈移动相反)。
0、数据结构定义:
#define M 100
typedef struct
{
StackElementType Stack[M];
StackElementType top[2];
/*top[0]和top[1]分别为两个栈顶指示器*/
}DqStack;
1、两栈共享的初始化操作算法
void InitStack(DqStack *S)
{
S->top[0] = -1;
S->top[1] = M;
}
2、 两栈共享的进栈操作算法
int Push(DqStack *S, StackElementType x, int i)
{
if(S->top[0]+1 == S->top[1])
return(FALSE); /*栈已满*/
switch(i)
{
case 0:
S->top[0]++;
S->Stack[S->top[0]] = x;
break;
case 1:
S->top[1]--;
S->Stack[S->top[1]] = x;
break;
default: return(FALSE)
}
return(TRUE);
}
3、 两栈共享的出栈操作算法
int Pop(DqStack *S, StackElementType *x, int i)
{
switch(i)
{
case 0:
if(S->top[0] == -1) return(FALSE);//判空
*x = S->Stack[S->top[0]];
S->top[0]--;
break;
case 1:
if(S->top[1] == M) return(FALSE); //判空
*x = S->Stack[S->top[1]];
S->top[1]++; break;
default: return(FALSE);
}
return(TRUE);
}
顺序栈缺点:需要预先分配空间。
① 如果数组预分配空间太小,容易造成溢出;
② 如果数组预分配空间太大,又会浪费空间;
链栈
用链表作为存储结构,不用预设栈空间大小。
为便于操作,采用带头结点单链表。
① 链表表头指针作为栈顶指针top,始终指向当前栈顶元素前面的头结点。若top->next=NULL
,则代表空栈;
② 链栈在使用完毕时,应该释放其空间。
0、结构类型:
typedef struct node
{
StackElementType data;
struct node *next;
}LinkStackNode;
typedef LinkStackNode *LinkStack;
2、链栈的进栈操作:
int Push(LinkStack top, StackElementType x)
{
LinkStackNode * temp;
temp = (LinkStackNode *)malloc(sizeof(LinkStackNode));
if(temp == NULL) return(FALSE); /* 申请空间失败 */
temp->data = x;
temp->next = top->next; //头插
top->next = temp; /* 修改当前栈顶指针 */
return(TRUE);
}
3、链栈的出栈操作:
int Pop(LinkStack top, StackElementType *x)
{ /* 栈顶元素弹出到x中 */
LinkStackNode * temp;
temp = top->next;
if(temp==NULL) return(FALSE);/*栈为空*/
top->next = temp->next;
*x = temp->data;//绕过temp
free(temp); /* 释放存储空间 */
return(TRUE);
}
栈的应用举例
1、数制转换:
对任意非负十进制数N,打印其等值二进制数。
void Conversion(int N)
{
Stack S; int x; /*S为顺序栈或链栈*/
InitStack(&S);
while(N>0)
{
x = N%2;
Push(&S, x);
N = N/2;
}
while(!IsEmpty(S))
{
Pop(&S, &x);
printf(“%d”, x);
}
}
2、 括号匹配问题
设置一个栈,扫描括号串:
① 若读入左括号,则直接入栈,等待相匹配的右括号;
② 若读入右括号,且与当前栈顶左括号匹配,将栈顶左括号出栈,否则属于不合法的情况。
如果输入序列已读尽,栈中仍有等待匹配的左括号,或者读入了一个右括号,而栈中已无等待匹配的左括号,均属不合法的情况。
当输入序列和栈同时变为空时,说明所有括号完全匹配。
void BracketMatch(char *str)
{
Stack S; int i; char ch;
InitStack(&S);
For(i=0; str[i]!='\0'; i++)
{
switch(str[i])
{
case '(': case '[': case '{':
Push(&S,str[i]);
break;//逢左进栈
case ')': case ']': case '}': //逢右出栈
if(IsEmpty(S))
{ printf("\n右括号多余!"); return;}
else
{
GetTop (&S, &ch);
if(Match(ch, str[i]))
Pop(&S, &ch);
else
{ printf("\n对应的左右括号不同类!"); return;}
}
}
if(IsEmpty(S))
printf("\n括号匹配!");
else
printf("\n左括号多余!")
}