栈是一种先进后出(后进先出)的线性表,其限制是仅允许在表的一端进行插入和删除运算。
源码文件下载:http://download.csdn.net/detail/mcu_tian/9530628
栈可以用链表实现,也可以用数组实现。
在使用
链表实现时,栈顶指针指向链表的前端节点,当栈顶指针为NULL时即为空栈。
在进行插入操作时,将节点插入链表的前端节点,并将栈顶指针指向插入节点。
在进行删除操作的时候,将节点栈顶指针指向的节点(非空)删除,栈顶指针指向后面的链表节点。
使用链表实现栈的方法所花费操作都是常数的时间,而且对栈的大小没有限制。
但是在进行插入、删除的操作的时候,malloc和free的调用开销太过于昂贵了
上面的缺点可以通过使用另外一个链表将空闲的或者栈删除的节点放入这个空闲链表(在最开始初始化时,预先在空闲链表放入若干节点),当要插入节点的时候,从空闲链表中取出节点插入栈链表中,当链表为空,才用调用malloc,申请新节点内存。这样可以避免频繁的malloc和free造成的昂贵的开销。
链表的实现:http://blog.csdn.net/mcu_tian/article/details/51496392
栈的数组实现是现在主要用的方法,这种缺点就是数组需要提前声明数组的大小,大小被限制了。
但是一般情况下,一个栈的元素不会太多,因此,只要比预先估计的大一点点,一般不会有堆栈溢出。
而且其实可以用类似于realloc类似的方法对数组进行动态扩充,可以克服上述的缺点。
栈使用数组的C语言实现如下,但是没有实现实现动态的扩充功能,只实现了最基本的功能。
数组实现对栈的操作为出栈(pop)和入栈(push),栈中的元素为int。
空栈的时候,不能进行pop
满栈的时候,不能进行push
栈的C语言实现以及操作如下:
struct
IntStack //栈的结构体
{
int
capacity; //栈的容量
int
top; //栈
顶位置
int
*
stackArray;//栈的数组
};
typedef struct IntStack *stack;
stack CreateStack(unsigned int stackNum)//创建一个栈
{
stack s;
s = (stack)malloc(sizeof(struct IntStack));
if(s == NULL)
{
printf("stack malloc failed\n");
return NULL;
}
s->stackArray = (int*)malloc(stackNum*sizeof(int));
if(s->stackArray == NULL )
{
printf("stack array failed\n");
return NULL;
}
s->
capacity
=
stackNum;
s->
top
=
-
1;//初始化的时候,栈顶的计数为-1
return s;
}
void
DisposeStack(
stack
*s) //释放栈,使用指针传递
s是为了保障释放后,指向栈的结构体的指针为NULL
{
if(*s != NULL)
{
free((*s)->stackArray);
free(*s);
*s = NULL;
}
}
int IsStackEmpty(stack s)//判断是否是空栈
{
return s->top == -1;
}
int
ResetStack(
stack
s)//清空栈,成功返回1,没成功返回-1
{
if(s != NULL)
{
s->top = -1;
return 1;
}
else
{
return -1;
}
}
int
IsStackFull(
stack
s) //栈是否满
{
return s->top == s->capacity-1;
}
int
StackPush(
stack
s,
int
*element) //element指向的数据入栈,之所以这样写,是为了与stacktop保持一致,参数传值,返回值为执行状态结果
{
if(IsStackFull(s))
{
return -1; //can't push
}
else
{
++s->top;
s->stackArray[s->top] = *element;
return 1; //ok
}
}
int StackPop(stack s)//出栈
{
if(IsStackEmpty(s)||(s == NULL))
{
return -1;
}
else
{
--s->top;
return 1;//ok
}
}
int
StackTop(
stack
s,
int
*element)//读取栈顶元素,放入element指针指向的整数中
{
if(!IsStackEmpty(s))
{
*element = s->stackArray[s->top];
return 1; //Ok
}
else
{
printf("Empty stack\n");
return -1;
}
}
栈的使用实例(C语言)
计算式的中缀表达式转换为后缀表达式
例如a+b*c+(d*e+f)*g 转换成后后缀表达式为abc*+de*f+g*+
c的实现如下,该函数的实现的功能为,从终端的输入中缀表达式
然后分析中缀表达式的字符串,将其转换为后缀的表达式,在终端上打印
该函数没有对输入的中缀式默认是合法的,没有对中缀式进行任何合法性的检查
中缀式的字符串长度小于100
同时只有+ 、*和(、)操作符,这样是为了降低其问题处理的规模,当然其实现的原理是差不多的,可以由以下的实现扩展开来。
对中缀式进行逐字符的读取
当为操作数的时候,立即输出,
当为操作符的时候不立即输出,比较栈顶的操作符的优先级的大小,若是比栈顶的优先级要高,则继续压栈
若是比栈顶的优先级要低,先将栈中的操作符弹出,并且输出,直到栈顶的操作符比该操作符优先级要低为止或者为空栈,然后将该操作符压栈。
这里只有+ *运算操作符,若是+,就将栈中的所有操作符弹出来,若是*则将其压入栈中。
当有左括号的时候,先将‘(’压栈,然后再遇到‘)’之前,跟上面操作一样,但是区别子在于栈顶操作符的比较为低优先级或者遇到‘(’终止比较,然后再压栈。
当为‘)’的时候,弹出栈顶操作符,并打印,等一直到遇到‘(’,并将‘(’弹出,但是并不打印,而且完成后‘)’不压栈。
当遍历完中缀式之后,栈若是不为空,则逐渐将栈顶元素弹出,并打印,直到栈为空。
具体如下实现
void InfixToPosfixDemo(void)//中缀表达式到前缀表达式的转换
{
stack s;
char expStr[100];
int strLen;
int i = 0;
s = CreateStack(100);
printf(" stack capacity:%d top:%d\n",s->capacity,s->top); //输入中缀式
scanf("%s",expStr);
strLen = strlen(expStr);
while(strLen)
{
int stmp;
stmp = expStr[i];
switch ((char)stmp) {
case '+':
{
while(!IsStackEmpty(s))//当低优先级的时候,将栈中的高于该优先级的操作符压栈
{
int tmp;
StackTop(s,&tmp);
if((char)tmp!='(')//若是有(,遇到(便终止比较弹出
{
StackPop(s);
printf("%c",tmp);
}
else
{
break;
}
}
StackPush(s,&stmp);
}
break;
case '*':
{
StackPush(s,&stmp);//为*,直接压栈
}
break;
case '(':
{
StackPush(s,&stmp);//直接压栈
}
break;
case ')':
{
int tmp;
StackTop(s,&tmp);
while((
char)tmp!=
'(') //弹出栈顶元素,打印,直到遇到(,并弹出,
b不打印
{
StackPop(s);
printf("%c",tmp);
StackTop(s,&tmp);
}
StackPop(s);
}
break;
default:
printf("%c",expStr[i]);
break;
}
++i;
--strLen;
}
while(!IsStackEmpty(s))//遍历结束,若不为空,则将堆栈中的元素弹出,并打印到终端
{
int tmp;
StackTop(s,&tmp);
StackPop(s);
printf("%c",tmp);
}
DisposeStack(&s);
if(s == NULL)
{
printf("\nDispose stack successfully\n");
}
}
运行截图如下: