您的位置:首页 > 其它

5--栈

2016-04-19 15:51 134 查看

Stack基本概念

栈是一种特殊的线性表,也就是具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表而已。又称为后进先出LIFO(Last In First Out)或先进后出(First In First Out)线性表。定义中说在线性表的表尾进行插入和删除操作,这里表尾是指栈顶,而不是栈底。

栈仅能在线性表的一端进行操作
栈顶(Top):允许操作的一端
栈底(Bottom):不允许操作的一端
它的特殊之处就是在与限制了这个线性表的插入和删除位置,它始终只在栈顶进行,也就是:栈顶是固定的,最先进栈的只能在栈底。
栈的插入操作,叫做进栈,也叫压栈、入栈。
栈的删除操作,叫做出栈,也叫弹栈。



Stack的常用操作

创建栈
销毁栈
清空栈
进栈
出栈
获取栈顶元素
获取栈的大小

#ifndef _MY_STACK_H_

#define _MY_STACK_H_

typedef void Stack;

Stack* Stack_Create();

void Stack_Destroy(Stack* stack);

void Stack_Clear(Stack* stack);

int Stack_Push(Stack* stack, void* item);

void* Stack_Pop(Stack* stack);

void* Stack_Top(Stack* stack);

int Stack_Size(Stack* stack);

#endif//_MY_STACK_H_

#include "stdlib.h"

#include "stdio.h"

#include "string.h"

#include "linkstack.h"

#include "linklist.h"

typedef struct _tag_LinkStackNode

{

LinkListNode node;

void *item;

}TLinkStackNode;

LinkStack* LinkStack_Create()

{

return LinkList_Create();

}

void LinkStack_Destroy(LinkStack* stack)

{

LinkStack_Clear(stack);

LinkList_Destroy(stack);

return ;

}

void LinkStack_Clear(LinkStack* stack)

{

while (LinkStack_Size(stack) > 0)

{

LinkStack_Pop(stack);

}

return ;

}

int LinkStack_Push(LinkStack* stack, void* item)

{

int ret = 0;

TLinkStackNode *node = (TLinkStackNode *)malloc(sizeof(TLinkStackNode));

if (node == NULL || item == NULL)

{

return -1;

}

node->item = item;

ret = LinkList_Insert(stack, (LinkListNode*)node, 0);

if (ret != 0)

{

free(node);

}

return ret;

}

void* LinkStack_Pop(LinkStack* stack)

{

void *ret = NULL;

TLinkStackNode *node = NULL;

if (stack == NULL)

{

return NULL;

}

node = (TLinkStackNode *)LinkList_Delete(stack, 0);

if (node == NULL)

{

return NULL;

}

ret = node->item;

//注意 释放内存不要忘记

if (node != NULL)

{

free(node);

}

return ret;

}

void* LinkStack_Top(LinkStack* stack)

{

void *ret = NULL;

TLinkStackNode *node = NULL;

if (stack == NULL)

{

return NULL;

}

node = (TLinkStackNode *)LinkList_Get(stack, 0);

if (node == NULL)

{

return NULL;

}

ret = node->item;

return ret;

}

int LinkStack_Size(LinkStack* stack)

{

return LinkList_Length(stack);

}


应用1:就近匹配

几乎所有的编译器都具有检测括号是否匹配的能力
如何实现编译器中的符号成对检测?
#include<stdio.h>int main()
{ int a[4][4]; int (*p)[4]; p = a[0]; return 0;

算法思路
从第一个字符开始扫描
当遇见普通字符时忽略,当遇见左符号时压入栈中
当遇见右符号时从栈中弹出栈顶符号,并进行匹配
匹配成功:继续读入下一个字符
匹配失败:立即停止,并报错
结束:
成功: 所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空
#include "stdio.h"
#include "stdlib.h"
#include "linkstack.h"
int isLeft(char c)
{
int ret = 0;
switch(c)
{
case '<':
case '(':
case '[':
case '{':
case '\'':
case '\"':
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
int isRight(char c)
{
int ret = 0;
switch(c)
{
case '>':
case ')':
case ']':
case '}':
case '\'':
case '\"':
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
int match(char left, char right)
{
int ret = 0;
switch(left)
{
case '<':
ret = (right == '>');
break;
case '(':
ret = (right == ')');
break;
case '[':
ret = (right == ']');
break;
case '{':
ret = (right == '}');
break;
case '\'':
ret = (right == '\'');
break;
case '\"':
ret = (right == '\"');
break;
default:
ret = 0;
break;
}
return ret;
}
int scanner(const char* code)
{
LinkStack* stack = LinkStack_Create();
int ret = 0;
int i = 0;
while( code[i] != '\0' )
{
if( isLeft(code[i]) )
{
LinkStack_Push(stack, (void*)(code + i));
}
if( isRight(code[i]) )
{
char* c = (char*)LinkStack_Pop(stack);
if( (c == NULL) || !match(*c, code[i]) )
{
printf("%c does not match!\n", code[i]);
ret = 0;
break;
}
}
i++;
}
if( (LinkStack_Size(stack) == 0) && (code[i] == '\0') )
{
printf("Succeed!\n");
ret = 1;
}
else
{
printf("Invalid code!\n");
ret = 0;
}
LinkStack_Destroy(stack);
return ret;
}
void main()
{
const char* code = "#include <stdio.h> int main() { int a[4][4]; int (*p)[4]; p = a[0]; return 0;";
scanner(code);
system("pause");
return ;
}

应用2:中缀后缀

计算机的本质工作就是做数学运算,那计算机可以读入字符串
“9 + (3 - 1) * 5 + 8 / 2”并计算值吗?
后缀表达式  ==?符合计算机运算
波兰科学家在20世纪50年代提出了一种将运算符放在数字后面的后缀表达式对应的,
我们习惯的数学表达式叫做中缀表达式===》符合人类思考习惯
实例:
5 + 4=> 5 4 +
1 + 2 * 3 => 1 2 3 * +
8 + ( 3 – 1 ) * 5 => 8 3 1 – 5 * +
中缀表达式符合人类的阅读和思维习惯
后缀表达式符合计算机的“运算习惯”
如何将中缀表达式转换成后缀表达式?

中缀转后缀算法:

遍历中缀表达式中的数字和符号
对于数字:直接输出
对于符号:
左括号:进栈 
运算符号:与栈顶符号进行优先级比较
若栈顶符号优先级低:此符合进栈  (默认栈顶若是左括号,左括号优先级最低)
若栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈
右括号:将栈顶符号弹出并输出,直到匹配左括号
遍历结束:将栈中的所有符号弹出并输出
中缀转后缀
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: