C / C++ 数据结构与算法 (栈与队列)

本文深入探讨了栈和队列两种基本数据结构的抽象数据类型、存储结构及应用。详细介绍了栈的顺序存储和链式存储,包括进栈、出栈操作,以及两栈共享空间的概念。同时,解析了队列的循环存储和链式存储,讨论了入队、出队操作。并通过斐波那契数列示例展示了栈在递归中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

只是我自己看书、视频的总结 可能不适合其他人看。

栈(stack)是限定仅在表位进行插入和删除操作的线性表。

栈的抽象数据类型

ADT 栈(stack)
Data
	同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
Operation
	InitStack (*s):初始化操作,建立一个空栈s;
	DestroyStack (*s):若栈存在,则销毁它;
	ClearStack(*s):将栈清空。
	StackEmpty(s): 若栈为空,则返回true,否则false。
	GetTop(s,*e): 若栈存在且非空,用e返回s的栈顶元素。
	Push(*s, e): 若栈s存在,插入新元素e到栈s中,并成为栈顶元素。
	Pop(*s, *e):删除栈s中栈顶元素,并用e返回其值。
	StackLength(s):返回栈s的个数。

栈的顺序存储结构

当栈存在一个元素时,top等于0;,因此通常把空栈的判定条件定为top等于-1。
代码:

typedef int SElemType;
typedef struct
{
	SElemType data[MAXSIZE];
	int top; //用于栈顶指针。
}SqStack;

当栈有两个元素,top=1; 栈有5个元素,top=4;空栈,top=-1。

进栈操作
Status Push(SqStack *S, SElemType e)
{
	if (S->top == MAXSIZE -1) //栈满
	{
		return ERROR;
	}
	S->top++;
	S->data[S->top]=e;//将新元素赋值给栈顶空间
	return OK;
出栈操作
Stastus Pop(SqStack *S, SEleType *e)
{
	if(S->top == -1)
		return ERROR;
	*e = S->data[S->top];//将要删除的元素赋值给e;
	S->top--;
	return OK;
}
两栈共享空间

数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为0处,另一个栈为栈的末端,即下标为数组长度n-1处。这样,两个栈如果增加元素,就是两端点向中间延伸。
若栈2是空栈,栈1的top1等于n-1时,就是栈1满了。反之,当栈1为空栈时,top2等于0时,为栈2满。两个栈见面之时,也即是两个指针之间相差1时,即top1 +1 == top2为栈满。
代码:

 typedef struct
 {
 	SElemType data[MAXSIZE];
 	int top1;
 	int top2;
 }SqDoubleStack;

 

对于两栈共享空间的push方法,我们除了要插入元素值参数外,还需要有一个判断是栈1还是栈2的栈号参数stackNumber。

Status Push(SqDoubleStack *S, SElemType e, int stackNumber)
{
	if (S->top1+1==S->top2) //栈满了
		return ERROR;
	if (stackNumber == 1) //栈1有元素进
		S->data[++S->top1] = e; //若栈1则先top1+1后给数组元素赋值
	else if (stackNumber == 2)//若栈2有元素进
		S->data[--S->top2]=e;//若栈2则先top2-1后给数组元素赋值。
	return OK;
}

两栈共享空间的pop方法,参数就之时判断栈1栈2的参数stackNumber
代码:

 Status Push(SqDoubleStack *S, SElemType e, int stackNumber)
 {
 	if (stackNumber==1)
 	{
 		if (S->top1==-1)
 			return ERROR;
 		*e = S->data[S->top1--];
 	}
 	else if (stackNumber==1)
 	{
 		if (S->top2== MAXSIZE)
 			return ERROR;
 		*e = S->data[S->top2++];
 	}
 return OK;
}

栈的链式存储结构

简称链栈。
通常对于链栈来说,是不需要头结点的。基本不存在栈满的情况,除非内存已经没有可以使用的空间了。
对于空栈来说,链表原定义是头指针指向空,那么链栈的空其实就是top=NULL的时候。

typedef struct StackNode
{
	SElemType data;
	struct StackNode *next;
}StackNode, *LInkStackPtr;

typedef struct LinkStack
{
	LinkStackPtr top;
	int count;
}LinkStack;
进栈操作
Status Push(LinkStacks *S, SElemType e)
{
	LinkStacksPtr s = (LinkStackPtr)malloc(sizeof(StackNode));
	s->data = e;
	s->next = S->top;//把当前的栈顶元素复制给新结点的直接后继
	S->top = s;//将新的结点s赋值给栈顶指针
	S->count++;
	return OK;
}
	
出栈操作
Status Pop(LinkStack *S, SElemType *e)
{
	LinkStackPtr p;
	if (StackEmpty(*s))
		return ERROR;
	*e = S->top->data;
	p=S->top;//将栈顶结点复制给p;
	S->top = S->top->next;//使得栈顶指针下移一位
	free(p);
	S->count--;
	return OK;
}

如果栈的使用过程中元素变化不可预料,有时很小,有时非常发,那么组好吃用链栈,繁殖,如果变化在可控范围内,建议使用顺序栈会更好一点。

栈的应用———递归

我们把一个直接调用自己或通过一系列的调用语句间接得调用自己的函数,称作递归函数。

斐波那契数列
# inclued <iostream>
using namespace std;

int Fbi(int i)
{
	if(i<2)
		return i == 0 ? 0 : 1;
	return Fbi(i-1) + Fbi(i-2);
}
int main()
{
	int i; 
	for(int i = 0; i < 40; i++)
		cout << Fbi(i) << endl;
	return 0;
}

队列

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
是FIFO(First In First Out)的线性表。

抽象数据类型

ADT 队列(Queue)
Data 
	同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
Operation
	InitQueue(*Q): 初始化操作,建立一个空队列Q。
	DestoryQueue(*Q):若队列Q存在,则销毁它。
	ClearQueue(*Q):将队列Q清空。
	QueueEmpty(Q):若队列Q为空,返回true,否则返回false。
	GetHead(Q, *e):若队列Q存在且非空,用e返回队列Q的队头元素。
	EnQueue(*Q, e):若队列Q存在,插入新元素e到队列Q中并成为队尾元素。
	DeQueue(*Q, e):删除队列Q中的队头元素,并用e返回其值。
	QueueLength(Q):返回队列Q的元素个数。
endADT

循环储存队列

把队列的头尾相接的顺序存储结构称为循环队列。
队列满的条件是**(rear+1)%QueueSize == fornt**
计算队列长度公式为
(rear-front+Queue)%QueueSize

代码:

typedef int QElemType
typedef struct
{
	QElemType data[MAXSIZE];
	int front;
	int rear;
}SqQueue;

循环队列的初始化代码:

Status InitQueue(SqQueue *Q)
{
	Q->front = 0;
	Q->rear = 0;
	return OK;
}

循环队列的入队操作代码:

Status EnQueue (SqQueue *Q, QElemType e)
{
	if ((Q->rear+1)%MAXSIZE == Q->front) //队列满的判断
		return ERROR;
	Q->data[Q->rear]=e;//将元素赋值给队尾
	Q->rear=(Q->rear+1)%MAXSIZE;//rear指针向后移动一个位置,若到最后则转到数组头部
	return OK;
}

循环队列的出队操作

Status DeQueue (SqQueue *Q, QElemType *e)
{
	if (Q->front == Q->rear)
		return ERROR;
	*e= Q->data[Q->front];//将头元素赋值给e
	Q->front=(Q->front+1)%MAXSIZE;//front指针向后移一个位置,若到最后则转到数组头部
	return OK;
}

链式存储结构

队列的链式存储结构,其实就是线性表放单链表,只不过它只能尾进头出。
将队头指针指向链队列的头结点。
代码:

typedef int QElemType;
typedef struct QNode 
{
	QElemType data;
	struct QNode *next;
}
QNode, *QueuePtr;

typedef Struct //队列的链表结构
{
	QueuePtr fornt, rear;
}LinkQueue;
入队操作
Status EnQueue (LinkQueue *Q, QElemType e)
{
	QueuePtr s=(QueuePtr)malloc(sizeof(QNode));
	if(!s)     //存储分配失败
		exit(OVERFOLE);
	s->data = e;
	s->next = NULL;
	Q->rear->next = s;   //把拥有元素e新结点s赋值给原队尾的后继
	Q->rear = s;  //把当前的s设置为队尾结点,rear指向s
	return OK;
}
出队操作

出队操作时, 就是头结点的后继结点出队,将头结点的后继改为它后面的结点。若链表处头结点外只剩一个元素时,则需要将rear指向头结点。

代码:

Status DeQueue(LinkQueue *Q, QElemType *e)
{
	QueuePtr p;
	if (Q->front == Q->rear)
		return ERROR;
		p=Q->front->next; //将删除的队头结点暂存给p
		*e= p->data;     // 将欲删除的队头结点赋值给e
		Q->front->next = p->next; // 将原队列头结点后继p->next赋值给头结点后继
		
		if (Q->rear == p) //若队头是队尾,则删除后将rear指向头结点
			Q->rear = Q -> front;
		free(p);
		return OK;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值