文章目录
一、Stack的基本概念
栈是一种特殊的i线性表栈仅能在线性表的一端进行操作
栈顶(Top):允许操作的一端
栈底(Bottom):不允许操作的一端
二、Stack的常用操作
创建栈
销毁栈
清空栈
出栈
获取站的元素大小
//C语言描述 ====>> 栈的实现与设计
#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_
三、栈模型和链表模型关系分析
线性表的顺序储蓄来模拟栈时,在尾部添加或删除大量元素,不会造成大数组的元素大量移动,所以在尾部插入删除元素比较好。
用线性表的链式存储栈的线性存储时,在链表头部插入元素或删除元素,不会因寻找链表尾节点而造成大规模遍历链表节点,所以在链表头部插入删除元素比较好。
四、栈的顺序存储设计与实现
基本概念
设计与实现
//头文件
#ifndef __MY_SEQLIST_H__
#define __MY_SEQLIST_H__
typedef void SeqList;
typedef void SeqListNode;
SeqList* SeqStack_Create(int capacity);
void SeqStack _Destroy(SeqStack * list);
void SeqStack _Clear(SeqStack * list);
int SeqStack _Length(SeqStack * list);
int SeqStack _Capacity(SeqStack * list);
int SeqStack _Insert(SeqStack * list, SeqListNode* node, int pos);
SeqListNode* SeqList_Get(SeqList* list, int pos);
SeqListNode* SeqList_Delete(SeqList* list, int pos);
#endif //__MY_SEQLIST_H__
五、栈的链式存储设计与实现
基本概念
设计与实现
//头文件
#ifndef _MY_LINKSTACK_H_
#define _MY_LINKSTACK_H_
typedef void LinkStack;
LinkStack* LinkStack_Create();
void LinkStack_Destroy(LinkStack* stack);
void LinkStack_Clear(LinkStack* stack);
int LinkStack_Push(LinkStack* stack, void* item);
void* LinkStack_Pop(LinkStack* stack);
void* LinkStack_Top(LinkStack* stack);
int LinkStack_Size(LinkStack* stack);
#endif //_MY_LINKSTACK_H_
六 、栈的应用
就近匹配
几乎所有的编译器都具有检车括号是否匹配的能力如何实现编译器中的符号成对检测?
算法思路:
当遇见第一个字符开始扫描
当遇见普通字符时忽略
当遇见左括号压入栈中
当遇见右符号从栈中弹出符号,并进行匹配
匹配成功:继续读入下一字节
匹配失败:立刻停止,并报错
结束
成功:所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空
当需要检测成对出现但又互不相邻的事物时
可以使用栈“后进先出”的特性
栈非常适合于需要“就近匹配”的场合
//就近匹配
#include <iostream>
#include <stack>
using namespace std;
#if 0
// 判断左括号
bool isLeft(char c)
{
bool bl;
switch (c)
{
case '[':
case '(':
case '{':
case '<':
bl = true;
break;
default:
bl = false;
break;
}
return bl;
}
bool isRight(char c)
{
bool bl;
switch (c)
{
case ']':
case ')':
case '}':
case '>':
bl = true;
break;
default:
bl = false;
break;
}
return bl;
}
bool match(char left, char right)
{
bool bl;
switch (left)
{
case '[':
bl = right == ']';
break;
case '(':
bl = right == ')';
break;
case '{':
bl = right == '}';
break;
case '<':
bl = right == '>';
break;
default:
bl = false;
break;
}
return bl;
}
void Jiujinpipei(const char* p)
{
int i = 0;
stack<char> st;
while (p[i] != '\0')
{
// 如果是左括号
if (isLeft(p[i]))
{
// 压栈
st.push(p[i]);
}
// 右括号
else if (isRight(p[i]))
{
if (!st.empty())
{
// 栈顶符号
char top = st.top();
// 匹配
if (!match(top, p[i]))
{
cout << "匹配失败!" << endl;
break;
}
st.pop();
}
else
{
cout << "缺少左括号..." << endl;
break;
}
}
i++;
}
if (p[i] == '\0' && st.empty())
{
cout << "匹配成功了...." << endl;
}
else
{
cout << "匹配失败!!!" << endl;
}
}
void main()
{
//
Jiujinpipei("#include <stdio.h> int main() int a[4][4]; int (*p)[4]; p = a[0]; return 0;}");
system("pause");
}
#endif;
中缀表达式和后缀表达式
计算机的本质工作就是做数学运算,那计算机可以读入字符串 “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 * +
中缀表达式符合人类的阅读和思维习惯;获罪表达式符合计算机的“运算习惯”。所以,如何将中缀表达式转换成后缀表达式?
中缀转后缀算法:
遍历中缀表达式中的数字和符号
对于数字,直接输出
对于符号:
左括号:进栈
运算符号:与栈顶符号进行优先级比较
若栈顶符号优先级低:此符合进栈(默认进栈若是左括号,左括号优先级最低)
若栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈
右括号:将栈顶符号弹出并输出,直到匹配左括号
遍历效果:将栈顶的所有符号弹出并输出
//中缀转后缀.cpp
#include<iostream>
#include<stack>
#include <cstring>
using namespace std;
#if 1
bool isNumber(char c)
{
return c >= '0' && c <= '9';
}
bool isOperator(char c)
{
return c == '+' || c == '-' || c == '*' || c == '/';
}
bool isLeft(char c)
{
return c == '(';
}
bool isRight(char c)
{
return c == ')';
}
int priority(char c)
{
int ret = 0;
switch (c)
{
case '+':
case '_':
ret = 1;
break;
case '*':
case '/':
ret = 2;
break;
default:
break;
}
return ret;
}
void Transform(const char* p)
{
int i = 0;
stack<char> st;
while (p[i] != '\0')
{
//数字
if (isNumber(p[i]))
{
// 直接输出
cout << p[i];
}
//左括号
if (isLeft(p[i]))
{
//进栈
st.push(p[i]);
}
//运算符
else if (isOperator(p[i]))
{
//优先级比较
while (!st.empty() && (priority(p[i]<=priority(st.top()))))
{
//输出
cout << st.top();
//出栈
st.pop();
}
//进栈
st.push(p[i]);
}
//右括号
else if (isRight(p[i]))
{
//如果不是左括号 输出并弹出
while (!isLeft(st.top()))
{
//输出
cout << st.top();
//出栈
st.pop();
}
//左括号出栈
st.pop();
}
i++;
}
//弹出栈内剩余符号
while (!st.empty())
{
//输出
cout << st.top();
//出栈
st.pop();
}
}
void main()
{
string str;
cin >> str;
Transform(str.data());
system("pause");
}
#endif // 1
计算机是如何基于后缀表达式计算的?
对于数字:进栈
对于符号: 从栈中弹出右操作数
从栈中弹出左操作数
l 根据符号进行运算
将运算结果压入栈中
遍历结果:栈中唯一数字作为计算结果
栈的神奇!
中缀表达式是人习惯的表达方式
后缀表达式是计算机喜欢的表达方式
通过栈可以方便的将中缀形式变换位后缀形式
中缀表达式的计算过程类似程序编译运行的过程