数据结构——6.栈

数据结构

6.栈

从数据结构角度看,栈和队列仍属于线性结构,具有线性结构的共同特征;
其个性是:栈和队列是操作受限的线性结构;

后进先出(LIFO,Last In First Out)或先进后出(FILO,First In Last Out)结构,最先(晚)到达栈的结点将最晚(先)被删除。

在这里插入图片描述

栈顶(top):表中允许进行插入和删除操作的一端称为栈顶;
栈底(bottom):表的另一端叫做栈底;
进栈(Push):在栈顶位置插入元素的操作叫进栈,也叫入栈、压栈
出栈(Pop):删除栈顶元素的操作叫出栈,也叫弹栈、退栈
栈溢出:当栈满时,若再有元素进栈则发生上溢;当栈空时,若再出栈则发生下溢。
空栈:不含元素的空表称为空栈。

1.2顺序栈

常用数组的后端表示栈顶,top常被称为栈顶指针;

栈空时,栈顶指针top==-1;

入栈时,栈顶指针加1,即++top;

出栈时,栈顶指针减1,即top–。
在这里插入图片描述

顺序栈的类型定义
template <class T>
class seqStack
{
	private:
		T* data;                       //存放栈中元素的数组
		int top;                       //栈顶指针,指向栈顶元素
		int maxSize;                   //栈的大小
	public:seqStack(int initSize=100);
	~seqStack(){delete[]data;}
	void clear(){top=-1;}              //清空栈
	bool empty()const{return top==-1;l}//判空
	int size()const{return top+1;}     //求长度
	void push(const T&value);          //进栈
	T pop();                           //出栈
	T getTop()const;                   //取栈顶元素
};

在这里插入图片描述
类内声明函数名,类外定义函数内具体的代码

初始化一个空的顺序栈,置栈顶指针top为-1
template <class T>
seqStack<T>::seqStack(int initSize=100){
	if(initSize<=0) throw badSize();
	data=new T[initSize];
	maxSize = initSize;
	top=-1;
}
	时间复杂度O(1),空间复杂度O(1)
1.进栈
template <class T>
void seqStack<T>s::push(const T&value){
	if(top == maxSize-1)
	{
		cout<<“栈满\n”;
		return;
	}
	data[++top] = value;      //++top!!!!!!!!!!
时间复杂度O(1),空间复杂度O(1)
2.出栈
template <class T>
T seqStack<T>::pop(){
	if(empty())throw outOfRange();//空栈无法弹栈		
    return data[top--];       //top--!!!!!!!!!!!
}
时间复杂度O(1),空间复杂度O(1)
应该指出:
	“出栈”并不一定要带回元素,带回元素是“副产品”,
    出栈的主要目的是下移指针,
    是否要返回栈顶元素取决于题目要求。
3.取栈顶元素
template <class T>
T seaStack-T>::getTop()const{
	if(empty())throw outOfRange();
	return data[top];
}
时间复杂度O(1),空间复杂度O(1)

1.3 链栈的表示和实现

栈的操作都是在栈顶进行的,用不带头结点的单链表就足够了

只需要考虑栈顶元素的插入删除,可将单链表的头指针指向栈顶。

在这里插入图片描述

链栈的类型定义
template <class T>
class linkStack{
private:
	struct Node{
	T data;
	Node* next;
	Node(){next=NULL;}
	Node(const T&value,Node *p = NULL)
	{data=value;next=p;}
};
Node* top;                                //栈顶指针
public:
	linkStack(){top = NULL;}              //初始化空栈
    ~linkStack(){clear();}
	void clear();                         //清空
	bool empty()const{return top == NULL;}//判空
    int size()const;                      //求长度
    void push(const T&value);             //压栈
    T pop();                              //弹栈
	T getTop()const;                      //取栈顶元素
};
1.进栈

template <class T>
void linkStack<T>::push(const T&value){//在栈顶插入元素
    Node *p = new Node();
    p->data = value;
    p->next = top;
    top = p;           //p成为新的栈顶元素
}
时间复杂度O(1),空间复杂度O(1)
2.出栈

在这里插入图片描述

template <class T>
T linkStack<T>::pop(){
	if(empty())throw outOfRange();//empty这个函数里可以写top==NULL
	Node *p = top;
	T value = p->data;//value保存栈顶元素的值
	top = top->next;  //top指向向后移动
	delete p;         //删除栈顶元素
	return value;
}
时间复杂度O(1),空间复杂度O(1)
3.取栈顶元素
template <class T>
T linkStack<T>::getTop()const{
	if(empty())throw outOfRange();
	return top->data;
}
时间复杂度O(1),空间复杂度O(1)
4.清空栈

在这里插入图片描述

template <class T>
void linkStack<T>::clear(){
	Node *p;
	while(top != NULL){
		p = top;//p指向当前栈顶元素
		top=top->next;//top指针移向次栈顶元素
		delete p;//释放p指向的当前栈顶元素
		}
}
时间复杂度O(n),空间复杂度O(1) 
5.求栈中元素的个数
template <class T>
int linkStack<T>::size()const{
	Node *p = top;
	int count = 0;//计数器
	while(p){     //遍历栈,统计元素总数
		count++;
		p=p->next;
	}
	return count;
}
时间复杂度O(n),空间复杂度O(1)

总结

1.栈是一种后进先出的线性表

2.栈可以用顺序实现,也可以用链接实现

3.大部分操作只需常数时间,顺序栈和链式栈在时间效率上难分伯仲

4.实际应用中,顺序栈比链栈用得更广泛些,因为顺序栈存储开销较低,并且能够以O(1)的时间复杂度快速定位并读取栈中元素,而在链栈中读取第i个元素时需要沿着指针查状,时间复杂度O(n)。

1.4栈的应用

中缀式求值

(中缀式指运算符在操作数中间的式子,例如a+b;后缀式顾名思义,例如ab+)

假定表达式中只有运算符和操作数两类成分,运算符有加+,减-,乘*,除/,以及圆括号()。

设置两个栈:

运算符栈(optr)(全名operator)、操作数栈(opnd)(全名operand)

算法思想:

首先置操作数栈opnd为空,将表达式起始符‘=’压入运算符栈optr作为栈底元素,然后从左向右扫描用于存储中缀表达式的infix数组,读入字符infix[i],直至表达式结束,

1.若infix[i]是数字字符就拼成数字,然后压入opnd栈

2.若infix[i]为运算符,则按以下规则进行:
(1)若infix[i]的优先级于optr栈顶的运算符,则infix[i]入optr栈
(2)若infix[i]的优先级于optr栈顶的运算符,则弹出optr栈顶的运算符,并从opnd栈弹出两个操作数,进行相应算数运算后,结果压入opnd栈;

在这里插入图片描述
在这里插入图片描述

中缀转后缀

后缀表达式的优点是计算机处理过程非常简单方便,这主要是因为:
(1)后缀表达式中不包含括号;
(2)不必考虑运算符的优先规则;
(3)运算符放在两个运算对象的后面,按照运算符出现的顺序,从左向右进行计算。

在这里插入图片描述

必会手工模拟

设一字符栈optr,并设中缀表达式和后缀表达式分别存放在字符数组infix和postfix中。从左向右扫描infix:
(1)若infix[i]是‘’,压入栈optr中;
(2)若infix[i]是操作数,将infix[i]直接送入postfix;
(3)若infix[i]是运算符,且其级别比栈顶元素的级别高,则infix[i]入栈;否则,栈顶元素退栈,进入postfix,infix[i]再与新栈顶元素比较,重复步骤(3);
(4)若infix[i]为‘’,则将栈中元素依次送postfix,直至碰到‘’,然后‘(’入栈,消掉一对括号。
(5)当遇到infix的结束符‘=‘时,开始执行退栈操作,并将退栈元素直接存入postfix,直至栈空。最后在postfix中存入\0’作为后缀表达式结束标志。

后缀式求值

对后缀表达式求值
(1)设置一操作数栈opnd;
(2)从左到右扫描后缀表达式,直到遇到结束标志‘\0’;
a)若读到的是操作数,则将其进栈;
b)若读到的是运算符,则将栈顶的两个操作数出栈,后弹出的操作数为被操作数,先弹出的为操作数,将得到的操作数完成运算符所规定的运算,并将结果进栈;
c)若读到的是空格,则跳过它;
(3)表达式扫描完毕,栈中剩一个数,即表达式的值。

附:中缀表达式转为后缀表达式——简便方法

**(1)加括号:**将中缀表达式中所有的子表达式按计算规则用嵌套括号括起来;
**(2)移运算符:**将每对括号中的运算符移到相应括号的后面;
**(3)删括号:**删除所有括号。
例如,将中缀表达式12*(6/2-0.5)转为后缀表达式:
步骤(1):(12*((6/2)-0.5));
步骤(2):(12((6 2)/0.5)-)*;
步骤(3):12 6 2 / 0.5 - *,即为所求后缀表达式。
可用类似方法将中缀表达式转为前缀表达式。

递归

递归是一种特殊的函数调用,是在一个函数中又调用了函数本身。
在系统内部,函数调用是用栈来实现,如果程序员可以自己控制这个栈,就可以消除递归调用。

递归过程的应用:

问题的定义是递归的:f(n)=n*f(n-1)

数据结构是递归的:链表

问题的解法是递归的:Hanoi塔问题

递归求解n的阶乘
int Fact(int n){
	if(n==0||n==1) return 1;
	else return (n * Fact(n-1));
}

在这里插入图片描述

//使用循环迭代方法,计算阶乘n!的程序

int fact(int n){
	int m=1;
	int i;
	if(n>1)
		for(i=1;i<=n;i++)
			m=m*i;
	return m;
}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值