从零开始的数据结构笔记(二)栈&队列

目录

一、栈

1.顺序栈

2.共享栈(也就是有两个栈顶指针,为了节约内存空间)

3.链栈

二、队列的顺序实现

1.入队时的逻辑

2.判断队满条件 

1.损失一个空间法

2.size法

3.0/1方法

 3.出栈&获取队头元素

三、队列的链式实现

1.带头结点的运算

2.不带头结点的运算

四、双端队列


一、栈

1.顺序栈

#include <stdio.h>
#define Maxsize 10
typedef struct
{
	ElemType data[Maxsize];
	int top;
}Sqstack;
void InitStack(Sqstack &q)		//初始化栈 只需要把栈顶指针变为-1就行 
{
	q.top=-1;
}
bool textStack(Sqstack &q)		//判断栈是否为空栈 
{	
	if(q.top ==-1)
	return true;				//栈空 
	else 			
	return false;				//栈非空 或者简写成return (q.top==-1) 
}
bool Push(Sqstack &q,ElemType x)//进栈操作 
{
	if(q.top ==Maxsize-1)
	return false;
	q.data [++q.top ]=x;	
	return true;
}
bool Pop(Sqstack &q,ElemType &x)	//出栈操作 
{
	if(q.top ==-1)
	return false;
	x=q.data [q.top --];
	return false;
} 
bool GetTop(Sqstack &q,ElemType &x)	//获取栈顶元素 
{
	if(q.top ==-1)					//若栈为空栈 则返回false 
	return false;
	x=q.data [q.top ];
	return true;					//注意top记得加q.top 还有++q.top和q.top--的用法 
}

注意top记得加q.top++q.top和q.top--的用法

这里的初始化操作是将top的值默认化为-1

另一种方式是将top的值化为0,注意此时入栈操作就是q.top++,出栈操作就是--q.top,还有判断空栈的条件 

注意题目要求

2.共享栈(也就是有两个栈顶指针,为了节约内存空间)

#include <stdio.h>
#define Maxsize 10
typedef struct
{
	ElemType data[Maxsize];
	int top0;
	int top1;
}Shstack;
void InitStack(Shstack &q)    //初始化共享栈
{
	q.top0=-1;
	q.top1=Maxsize;
}
bool TextStack(Shstack &q)    //判断栈是否满
{
	if(q.top0+1==q.top1)
	return true;
	else 
	return false;
}

注意一点 就是以静态分配内存的方式 函数运行结束后会自动回收内存

动态分配内存的方式 就需要malloc和free函数

3.链栈

这里的链栈是以有带头结点来实现的 而链栈最好还是用不带结点的来实现

1.带头结点

#include <stdio.h>
#include <stdlib.h>
typedef struct StackNode
{
	ElemType data;
	struct StackNode* next;
}StackNode,*LinkStack;
bool InitStack(LinkStack &q)
{
	q=(StackNode *)malloc(sizeof(StackNode));		//这里是有头结点的类型
	if(q==NULL)
	return false;
	q->next=NULL;
	return true;
}
bool PushStack(LinkStack &q,ElemType x)				//进栈操作
{
	StackNode *p;
	p=(StackNode *)malloc(sizeof(StackNode));
	if(p==NULL)
	return false;
	p->data=x;
	p->next=q;
    q=p;
	return true;
} 
bool PopStack(LinkStack &q,ElemType &x)
{
	if(q->next==NULL)
	return false;
	StackNode *p;
	p=q;							//用p来储存栈顶指针以便栈顶指针被释放 
	x=q->data;
	q=q->next;
	free(p);
	return true;
}

2.不带头结点

#include <stdio.h>
#include <stdlib.h>
typedef struct StackNode
{
	ElemType data;
	struct StackNode* next;
}StackNode,*LinkStack;
void InitStack(LinkStack &q)
{
	q=NULL;
	return 0;
}
bool PushStack(LinkStack &q,ElemType x)
{
	StackNode *p;
	p=(LinkStack)malloc(sizeof(StackNode));
	if(p==NULL)
	return false;
	p->data=x;
	if(q==NULL)
	{
		p->next=NULL;
		q=p;
	}
	p->next=q;
    q=p;
	return true;
}
bool PopStack(LinkStack &q,ElemType &x)
{
	if(q==NULL)
	return false;
	x=q->data;
	StackNode *p;
	p=q;
	q=q->next;
	free(p);
	return true;
}

二、队列的顺序实现

1.入队时的逻辑

因为是队列 所以总该有人出队 总有人入队 入队的人插到队尾

简写成FIFO(First in First out) 

 所以出队时front上移 有空的空间 当队尾的值到达最大长度时

就要回到下面来 看看还有没有剩余的空间 所以在这里要用到%符号

%的意思就是:比如说 x%10 也就是(x%10)这个表达式的值的范围就是0~9

所以具体的代码实现就是

typedef struct Queue
{
    ElemType data[Maxsize];
    int front;
    int rear;
}Queue;
bool PushQueue(Queue &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;
}

这实际上就是把队列逻辑上变成一个环形的队列 使队尾指针无限在0~(Maxsize-1)的范围一直 循环直到队列已满

由于我的代码是    Q.data[Q.rear]=x;
                             Q.rear=(Q.rear+1)%Maxsize;

是先赋值然后再进行队尾指针的后移

也就是 我的队尾指针永远指向接下来要插入的元素的位置

所以队满的条件就是 当再进行队尾指针后移的时候和队头指针的值相同

也就是(Q.rear+1)%Maxsize==Q.front;

这时候就要返回一个错误

 更形象化的就是 如右图:

但是这样判断队满条件的缺点就是会白白浪费一个存储空间 

具体的进队操作如下

#include <stdio.h>
#include <stdlib.h>
#define Maxsize 10
typedef struct Queue
{
	ElemType data[Maxsize];
	int front;
	int rear;	
	int length;
}Queue;
bool InitQueue(Queue &Q)
{
	Q.front=0;
	Q.rear=0;
	Q.length=0;
	return true;
}
bool TestQueue(Queue &Q)	//判断是否是为空队列 
{
	if(Q.front==Q.rear)
	return true;
	else
	return false;
}
bool PushQueue(Queue &Q,ElemType x)			//入队操作 
{
	if(Q.length==Maxsize)
	return false;							//队列已满 
	Q.data[Q.rear]=x;
	Q.rear=(Q.rear+1)%Maxsize;				//注意这个'%'符号 将逻辑变成环形的 好好品 
	Q.length++;
	return true;
}

2.判断队满条件 

注:一下情况都是队尾指针指向下一个元素插入时的位置 而不是队列的尾部

1.损失一个空间法

如上面所说的队满条件判断

具体实现就是 if((Q.rear+1)%Maxsize==Q.front);

                       return false;

2.size法

就是在定义队列的结构体的内容中加一个int变量的来记录队列的长度

typedef struct Queue
{
	ElemType data[Maxsize];
	int front;
	int rear;	
	int length;            //lenght变量来记录队列的长度
}Queue;

所以同样的在初始化队列的同时也要将lenght变量的值赋值为0

具体实现就是 if(Q.lenght==Maxsize)

                        return false;

3.0/1方法

 定义一个tag变量来记录操作的是入队还是出队

入队就是1 出队就是0

所以如果要判断队满的条件那就是要tag的值为1同时Q.rear==Q.front

typedef struct Queue
{
	ElemType data[Maxsize];
	int front;
	int rear;	
	int tag;            //最近进行的是插入/删除
}Queue;

具体实现就是 if(Q.tag=1&&Q.rear==Q.front)

                        return false;

 3.出栈&获取队头元素

#include <stdio.h>
#include <stdlib.h>
#define Maxsize 10
typedef struct Queue
{
	ElemType data[Maxsize];
	int front;
	int rear;
}Queue;
void InitQueue(Queue &Q)
{
	Q.front=0;
	Q.rear=0;
	return 0;
}
bool PopQueue(Queue &Q,ElemType &x)         //出栈操作
{
	if(Q.front==Q.rear)		            	//判断是否为空队列 
	return false;				
	x=Q.data[Q.front];
	Q.front=(Q.front+1)%Maxsize; 
	return false; 
}
bool GetTop(Queue &Q,ElemType &x)        //获取队头元素
{
	if(Q.front==Q.rear)			         //队空则报错 
	return false;
	x=Q.data[Q.front];
	return true;
}

三、队列的链式实现

头指针都是指向第一个元素

1.带头结点的运算

这里的头指针是指向头结点 而不是队头元素

#include <stdio.h>
#include <stdlib.h>
typedef struct ListQueue
{
	ElemType data;
	struct ListQueue* next;
}QueueNode,*ListQueue;
typedef struct
{
	ListQueue front;
	ListQueue rear;
}LQueue;
bool InitQueue(LQueue &Q)
{
	Q.front=(ListQueue)malloc(sizeof(QueueNode));
	Q.rear=(ListQueue)malloc(sizeof(QueueNode));
	if(Q.front==NULL||Q.rear==NULL)
	return false;
	Q.front=Q.rear;
	Q.front->next=NULL;
	return true;
} 
bool PushQueue(LQueue &Q,ElemType x)		//入队操作 
{
	ListQueue p;
	p=(ListQueue)malloc(sizeof(QueueNode));
	if(p==NULL)
	return false;
	p->data=x;
	p->next=Q.rear->next;			//也可以写成 p->next=NULL;一样的 
	Q.rear=p;
	return true;
}
bool PopQueue(LQueue &Q,ElemType &x)		//出队操作 
{
	if(Q.rear==Q.front)
	return false;
	x=Q.front->data;
	ListQueue p;
	p=(ListQueue)malloc(sizeof(QueueNode));
	p=Q.front->next;
	Q.front->next=p->next;
	if(Q.rear==p)					//当这个结点是最后一个元素的时候 要将Q.rear和Q.front相同 也就是空队 
	Q.rear=Q.front;
	free(p);
	return true;
} 

2.不带头结点的运算

#include <stdio.h>
#include <stdlib.h>
typedef struct QueueNode
{
	ElemType data;
	struct QueueNode* next;
}QueueNode,*LinkQueue;
typedef struct
{
	LinkQueue front;
	LinkQueue rear;
}LQueue;
void InitQueue(LQueue &Q)
{
	Q.front=NULL;		
	Q.rear=NULL;
	return 0;
}
bool PushQueue(LQueue &Q,ElemType x)
{
	LinkQueue p;
	p=(LinkQueue)malloc(sizeof(QueueNode));
	if(p==NULL)
	return false;
	p->data=x;								//将新结点的数据存入 
	p->next=NULL;							//将新结点的地址改为NULL 
	if(Q.front==NULL)						//空队时单独讨论 
	{
		Q.front=p;							//队头指针都是指向第一个结点 注意带头结点的是有空结点的 
		Q.rear=p;
	}
	else
	{
		Q.rear->next=p;						//新的结点插入队尾 
		Q.rear=p;							//修改新的队尾指针 
	}
	return true;
}
bool PopQueue(LQueue &Q,ElemType &x)
{
	if(Q.front=NULL)						//空队 
	return false;
	x=Q.front->data;
	LinkQueue p;
	p=Q.front;
	if(p==Q.rear)
	{
		Q.front=NULL;
		Q.rear=NULL;
	}
	Q.front=Q.front->next;
	free(p);
	return true;
}

不带头结点的队列中 仅存一个元素的条件是 Q.front==Q.rear注意和带头结点的区分

四、双端队列

双端队列分为三种

只要记住判断输出序列合法性的考法就行

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值