数据结构之Stack


一、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 根据符号进行运算
  将运算结果压入栈中
遍历结果:栈中唯一数字作为计算结果

在这里插入图片描述

栈的神奇!
  中缀表达式是人习惯的表达方式
  后缀表达式是计算机喜欢的表达方式
  通过栈可以方便的将中缀形式变换位后缀形式
  中缀表达式的计算过程类似程序编译运行的过程



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值