【数据结构】栈与队列

总结

栈队列知识导图

1.顺序栈

基本操作:

typedef int ElemType;

//定义:顺序栈// 
typedef struct{
	ElemType data[Maxsize];
	int top;//栈顶指针 
}SqStack;
 
//判空// 
bool StackEmpty(SqStack S){
	if(S.top == -1)	return true;//大小都是负的,肯定是空的// 
	else return false; 
} 
 
//进栈// 
bool Push(SqStack &S , ElemType x){
	if(S.top == Maxsize - 1)	return false;
	S.data[++S.top] = x;	return false;//这个地方是先执行了移动操作,后执行了赋值操作 
} 
 
//出栈操作 
bool Pop(SqStack &S , ElemType &x){//这个地方的x也是引用操作 
	if(S.top == Maxsize - 1)	return false;
	x = S.data[S.top --];//先去赋值,然后执行移动指针的操作// 
	return true;
}
 
//读取栈顶元素
bool GetTop(SqStack S , ElemType &x){//x:引用操作 
	if(S.top == -1)	return false;
	x = S.data[S.top];
	return true;
} 

(易混问题)指针问题:
入栈:指针先移动,后赋值进来:先告诉东西往哪里放,再放东西。
出栈:先赋值出去,后移动指针:先取出来东西,再移动指针。
 

2.共享栈

头尾都是一个入栈的口,最后指针相邻则判满;指针移动和顺序栈的要求一致,用的少。

typedef int ElemType;
 
//定义:共享栈// 
typedef struct{
	ElemType data[Maxsize];
	int top1;//栈顶指针1 
	int top2;//栈顶指针2 
}SqStack;
 
//判满// 
bool StackFull(SqStack S){
	if(S.top1 + 1 == S.top2)	return true;
	else return false; 
} 
 
//进栈// 
bool Push(SqStack &S , ElemType x , int stackNum){
	if(S.top + 1== S.top2)	return false;
	if(stackNum == 1)	S.data[++S.top1] = x;//执行了++操作,后执行了赋值操作 
	if(stackNum == 2)	S.data[--S.top2] = x;//注意这个操作// 
	return true; 
} 
 
//出栈操作 
bool Pop(SqStack &S , ElemType &x , int stackNum){//这个地方的x也是引用操作 
	if(S.top1 == -1 || S.top2 == Maxsize + 1)	return false;
	if(stackNum == 1)	x = S.data[S.top1 --];//先去赋值,然后执行移动指针的操作// 
	if(stackNum == 2)	x = S.data[S.top2 ++];
	return true;
}
 
//读取栈顶元素
bool GetTop(SqStack S , ElemType &x , int stackNum){//x:引用操作 
	if(S.top1 == -1 || S.top2 == -1)	return false;
	if(stackNum == 1)	x = S.data[S.top1];
	if(stackNum == 2)	x = S.data[S.top2];
	return true;
} 

 

3.链栈

防上溢。

typedef int ElemType;
 
//链栈结点的定义 
typedef struct SNode{
	ElemType data;//栈中存放的元素 
	struct SNode *next;//下一个结点 
}SNode , *SLink;//链栈的结点
 
//链栈的定义 
typedef struct LinkStack{
	SLink top;//栈顶指针 
	int count;//栈的个数 
}LinkStack;
 
//进栈  
bool Push(LinkStack *S , ELemType x){
	SLink p = (SLink)malloc(sizeof(SNode));//malloc要求返回字节数,并且强制转换为实际的指针类型 
	p->data = x;//如果是指针访问,用->;如果是对象访问,用.// 
	p->next = S->top;//next指向当前栈顶 
	S->top = p;//栈顶指针指向新的元素// 
	S->count ++;
	return true; 
} 
 
//出栈操作
bool Pop(LinkStack *S , ElemType x){
	if(S->top == -1)	return false;
	x = S->top->data;
	SLink p = S->top;	//设置一个指针指向栈顶元素// 
	S->top = S.top->next;//top指针向后移动// 
	free(p); //释放指针// 
	S->count --;
	return true; 
} 

 
 

队列

1.基本队列

 

2.循环队列
typedef int ElemType;
 
//循环队列的定义 
typedef struct{
	ElemType data[Maxsize];
	int front , rear;//队头指针,队尾指针// 
}SqQueue;
 
//入队操作// 
bool EnQueue(SqQueue &Q , ElemType x){
	if((Q.rear + 1) % Maxsize == Q.front)	return false;
	Q.data[Q.rear] = x;
	Q.rear = (Q.rear + 1) % Maxsize;//算出队尾的位置而不是移动// 
	return true;
} 
 
//出队//
bool DeQueue(SqQueue &Q , ElemType &x){
	if(Q.rear == Q.front)	return false;//循环队列// 
	x = Q.data[Q.front];
	Q.front = (Q.front + 1) % Maxsize;
	return true;
}

(常考问题)判满问题:
1.在循环队列中,Q.front指向了第一个元素,Q.rear则指向了最后一个元素的后一位,一旦满了,势必导致指针溢出。同时又不能像栈一样再开辟空间,因为前面的资源没有用完啊(你有什么脸再让人家开新的空间╮(╯▽╰)╭)。为了充分利用好前面的空位,所以出现了循环队列。
2.循环队列的问题在于,如果Q.front == Q.rear话,居然会出现分别不清到底现在是满的的空的这种问题。(这个问题有点像时钟,给你个时钟,你说现在是14:00还是2:00。。分不清的)
所以就有了下面的这几种方法。

方法1: 牺牲一个空间滞空。
1.若Q.rear的下一个(即 Q.rear+1 )指针指向的是Q.front,则认为是满了。
2.空的话依旧是Q.front == Q.rear,即为刚开始的时候。

//队满
//这种方法可以“归0”,满了一个“轮回Maxsize”,Q.rear+1 就回到起点。 
(Q.rear + 1) % Maxsize == Q.front

//队空
Q.front == Q.rear

//队中元素个数
(Q.rear - Q.front + Maxsize) % Maxsize

//删除
Status Dequeue(Sequeue &Q , QElemtype &e){
	if(Q.front == Q.rear)	return ERROR;
	e = Q.base[Q.front]
	Q.front = (Q.front + 1) % Maxsize;	//所有的移动都需要这么移动,防止这个数字溢出
	return OK;
}

方法二: 增加一个队满元素数目

//队满
Q.size == Maxsize

//队空
Q.size == 0

//队伍大小
Mazsize

方法三: 增加tag标记

//插入后队满:
if(tag == 0 && Q.front == Q.rear)	return true;

//插入后队空:
if(tag == 1 && Q.front == Q.rear)	return false;	

 

3.链式队列

1.在删除模块的时候,需要保证链队列的Q.rear 指针不会丢失。
2.该队列带头节点。

typedef int ElemType;
 
//链式队列结点的定义 
typedef struct{
	ElemType data;
	struct LinkNode *next;//这个也是一个结点指针// 
}LinkNode;
 
//链式队列的定义 
typedef struct{
	LinkNode *front , *rear;
}LinkQueue;
 
//入队操作// 
void EnQueue(LinkQueue &Q , ElemType x){
	LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;
	Q.rear->next = s;
	Q.rear = s;
} 
 
//出队:出队指的是头结点的后继结点出队//
bool DeQueue(LinkQueue &Q , ElemType &x){
	if(Q.front == Q.rear)	return false;
	LinkNode *p = Q.front->next;
	x = p->data;
	Q.front->next = p->next;
	if(Q.rear == p)	Q.rear = Q.front;//单独只有一个数据的时候,删了p会把rear报错,往前放// 
	free(p);	 
	return true; 
}

 

4.双端队列

前开,后开,两边都开。
 
 

应用

1.栈的应用

括号匹配

#include<iostream>
#include<stack>
using namespace std;
 
bool Check(string str){
	stack<char> s;
	for(int i = 0 ; i < str.length() ; i ++){
		switch(str[i]){
			case '(':
			case '[':{	s.push(str[i]);	break;	}
			case ')':{
				if(s.top() == '('){	s.pop();	break;}	
				else	return false;
			}
			case ']':{
				if(s.top() == '['){	s.pop();	break;}	
				else	return false;
			}			
		}
	}
	if(s.empty() == true){
		printf("It is true!");
		return true;
	}	
	else{
		printf("It is false!");
		return false;
	}	
} 
 
int main(){
	string s = "(())";
	int a = Check(s);
	return 0;
	
}

后缀表达式 (稍后补上)

递归
阶乘运算

#include<iostream>
using namespace std;
 
int F(int a){
	if(a == 0)	return 1;
	else return a * F(a - 1);
}
 
int main(){
	int a = 4;
	printf("%d" , F(a));
	return 0;
}

斐波那契

#include<iostream>
using namespace std;
 
int Fib(int a){
	if(a == 0)	return 0;
	else if(a == 1)	return 1;
	else return Fib(a - 1) * Fib(a - 2);
}
 
int main(){
	int a = 4;
	printf("%d" , Fib(a));
	return 0;
}

 

2.队列应用

层序遍历: 参考树的操作一章节。
系统应用: 缓存区。 (堆栈问题???保存一下后面找到了补上。)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值