#数据结构(二)--栈和队列

栈和队列

一栈

1.栈的顺序存储结构

特点:先进后出

栈是一种只能在一端进行插入或删除操作的线性表。

表中允许插入删除操作的一端为栈顶(top),表的另一端为栈底(bottom),

1 结构体的定义
cpp 复制代码
#include <stdio.h>
typedef int ElemType;
#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE]; // 用于存储栈中的元素
    int top; // 栈顶指针,指向栈顶元素的索引
} SqStack;
2 初始化栈

栈顶指针赋值为-1

cpp 复制代码
void InitStack(SqStack *&s) {
    s = (SqStack *) malloc(sizeof(SqStack));//申请一个顺序栈空间
    s->top = -1;//栈顶指针赋值为-1
}
3 销毁栈

释放节点空间

cpp 复制代码
void DestroyStack(SqStack *&s) {
    free(s);
}
4 判断栈是否为空

指针为-1

cpp 复制代码
bool StackEmpty(SqStack *&s) {
    return (s->top == -1);
}
5 进栈

先判断栈是否为满

注意先将指针++,再对栈顶元素进行赋值

cpp 复制代码
bool PushStack(SqStack *&s, ElemType e) {
    if (s->top == MAX_SIZE - 1) {//栈满的情况
        return false;
    }
    s->top++;
    s->data[s->top] = e;//先加再赋值(s-data[++s->top])
    return true;
}
6 出栈

先对栈元素赋值,再将栈顶指针--

cpp 复制代码
bool Pop(SqStack *&s, ElemType &e) {
    if (s->top == -1) {//栈空的情况
        return false;
    }
    e=s->data[s->top];
    s->top--;
    return true;
}
7 获取栈顶元素
cpp 复制代码
bool GetTop(SqStack *s,ElemType &e){
    if (s->top==-1)
        return false;
    e=s->data[s->top];
    return true;
}
补充:共享栈
  1. 顺序栈采用一个数组存放栈中的元素。如果需要用到两个类型相同的栈,这时若为它们各自开辟一个数组空间,极有可能出现这样的情况:第一个栈已经满了,再进栈就溢出了,而另一个栈还有许多的空间。
  2. 共享栈是为了更有效地利用存储空间,两个栈的空间相互调节,只有在整个存储空间被占满时才会发生上溢。其存储数据的时间复杂度均为O(1),所以对存取效率没有什么影响。
  3. 栈空条件:栈0空为top==-1;栈1空为top1==MaxSize.
  4. 栈满条件:top==top1-1
  5. 元素进栈操作:进栈0操作为top0++;data[top0]=x;进栈1操作为top1--;data[top1]=x
  6. 元素出栈操作:出栈0操作为x=data[top0];top0--;出栈1操作为x=data[top1];top1++
  7. 在上述操作中,data数组表示共享栈的存储空间,top0和top1分别为两个栈的栈顶指针,这样该共享栈通过data,top0和top1来标识,也可以将他们设计为一个结构体类型。

2.栈的链式存储结构

栈中的数据元素的逻辑关系呈线性关系,所以栈可以像线性表一样采用链式存储结构

链栈通过单链表实现

不存在栈满上溢的情况,可以一直添加。

cpp 复制代码
typedef struct linknode
{
    //定义链表节点数据
    ElemType data;
    //定义指针域,单链表的后继指针域
    struct linknode *next; 
}LiStack;

栈的四要素:

栈空的条件:

s->next==NULL

cpp 复制代码
bool StackEmpty(LiStack *s)
{
    return(s->next == NULL);
}
栈满的条件:

链栈不存在满的情况

进栈的条件

将带新元素的节点插入

cpp 复制代码
//定义链表节点指针
LiStack *p;
//为新节点申请空间
p = (LiStack *)malloc(sizeof(LiStack));
//为新节点的数据区赋值
p -> data = e;
//头插法
//将新节点后继指针指向下一个节点 ,下一个节点插入前存放在头结点的后继指针
p -> next = s ->next;
//然后将头结点的后继指针指向新节点
s->next = p;
出栈的条件:

将栈顶节点删除

cpp 复制代码
//传入要出栈的链栈 , 传入要出栈的指针元素
bool Pop(LiStack *&s , ElemType &e)
{
	//用指针指向要出栈的元素,就是头结点的后继节点
	LiStack *p;
	//如果s->next 为空,则表明为空栈 ,无法出栈元素
	if(s->next == NULL)
	{
		return false;
	}
	//如果经过上面条件的验证,s->next不为空,则接着往下走
	//先定位要出栈的元素
	p = s->next;	//头结点的后继节点
	e = p->data;	//栈顶数据传出
	//开始删除头结点的后继节点 p,也就是头结点指向要删除节点的下一个节点
	s->next = p->next;
	//然后释放 p 空间
	free(p);
	return true;
}

二 队列

特点:先进先出

队列只允许在一端进行插入另一端进行删除,允许插入的时队尾,允许删除的时队头

插入元素称为入队,删除元素称为出队。

1 顺序队列

  • 用一组地址连续的存储单元,以此存放从队头到队尾的数据元素,称为顺序队列。

  • 需要附设两个指针,一个队头指针(front)一个队尾指针(rear),分别指向队头队尾。

  • 初始时front和rear均为-1

  • 元素进队rear加1

  • front指向队首元素的前一位置

  • 队满条件:rear==MaxSize-1

  • 队空条件:front==rear

  • 进队操作:元素进队rear++,data[rear]=e

  • 出队操作:元素出队front++,e=data[front]

顺序队中实现队列的基本运算算法

结构体声明

cpp 复制代码
typedef struct {
    ElemType data[MaxSize];//存放队中元素
    int front,rear;//队头和队尾指针
}SqQueue;
1 初始化

构造一个空队列q,将front和rear指针均设置为初始状态-1

cpp 复制代码
void InitQueue(SqQueue *&q){
    q=(sqQueue *)malloc(sizeof(SqQueue));
    q->front=q->rear=-1;
}
2 入队

在队不满的情况下,先将队尾指针rear++,再将元素添加到该位置

cpp 复制代码
bool enQueue(SqQueue *&q, ElemType e) {
    if (q->rear == MaxSize - 1)
        return false;
    q->rear++;
    q->data[q->rear] = e;
    return true;
}
3 出队

在队不空的情况下,将队首元素front++,再将该位置的元素赋值给e

cpp 复制代码
bool deQueue(SqQueue *&q, ElemType &e) {
    if (q->front == q->rear)
        return false;
    q->front++;
    e = q->data[q->front];
    return true;
}

2 环形队列:

这是因为采用了rear=MaxSize-1作为队满条件的缘故,当出现这种情况可能会出现还有空间没有填满的情况。这种情况称为假溢出。

解决方案:环形队列

实际上地址一定是连续的不可能是环形的,这里是通过逻辑方式实现环形队列

  • rear=(rear+1)%MaxSize
  • front=(front+1)%MaxSize
  • 队空条件:rear==front
  • 队满条件:(rear+1)%MaxSize==front
  • 进队操作:rear=(rear+1)%MaxSize,并且将e放在rear处
  • 出队操作:front=(front+1)%MaxSize,将front位置e取出
  • 这种情况下会比原先少放一个元素

相关计算:

3 链队

  • 采用链表存储的称为链队。

相关操作:

单链表中数据节点类型DataNode声明如下

cpp 复制代码
typedef struct qnode{
    ElemType data;
    struct qnode *next;
}DataNode;

链队节点类型LinkQuNode声明如下

cpp 复制代码
typedef struct {
    DataNode *front;
    DataNode *rear;
}LinkQuNode;

链队四要素:

  • 链队为空的判断条件:front=rear=NULL
  • 链队为满的判断条件:不考虑
  • 链队插入的判断条件:将含e的单链表节点插入至链表结尾
  • 链队删除的判断条件:将单链表的首元节点删除

代码实现;

cpp 复制代码
#include <stdio.h>
#include <cstdlib>

typedef int ElemType;
typedef struct qnode {
    ElemType data;//存放元素
    struct qnode *next;//下一个节点指针
} DataNode;//数据节点的类型

typedef struct {
    DataNode *front;
    DataNode *rear;
} LinkQuNode;
初始化:
cpp 复制代码
void InitQueue(LinkQuNode *&q) {
    q = (LinkQuNode *) malloc(sizeof(LinkQuNode));
    q->front = q->rear = NULL;
}
判断是否为空:
cpp 复制代码
bool QueueEmpty(LinkQuNode *q) {
    return (q->rear == NULL);
}
进队列
cpp 复制代码
bool enQueue(LinkQuNode *&q, ElemType e) {
    DataNode *p;
    p = (DataNode *) malloc(sizeof(DataNode));
    p->data = e;
    p->next = NULL;
    if (q->rear == NULL)
        q->front = q->rear = p;
    else {
        q->rear->next = p;
        q->rear = p;
    }
    return true;
}
出队列
cpp 复制代码
bool deQueue(LinkQuNode *&q, ElemType &e) {
    DataNode *t;
    if (q->rear == NULL)
        return false;
    t = q->front;
    if (q->front == q->rear)
        q->front = q->rear = NULL;
    else
        q->front = q->front->next;
    e = t->data;
    free(t);
    return true;
}

4 双端队列

所谓双端队列是指两端都可以进行进队和出队的操作的队列,将队列的两端分别成为前端和后端,两端都可以进队和出队。

相关推荐
长潇若雪8 分钟前
结构体(C 语言)
c语言·开发语言·经验分享·1024程序员节
DARLING Zero two♡42 分钟前
关于我、重生到500年前凭借C语言改变世界科技vlog.12——深入理解指针(2)
c语言·开发语言·科技·1024程序员节
黑不拉几的小白兔2 小时前
PTA L1系列题解(C语言)(L1_097 -- L1_104)
数据结构·算法·1024程序员节
环能jvav大师2 小时前
使用Ubuntu系统+VS Code开发STC51单片机
linux·c语言·开发语言·单片机·嵌入式硬件·ubuntu
秋说2 小时前
【数据结构 | PTA】懂蛇语
数据结构·c++
黄交大彭于晏3 小时前
第五天学习总结:C语言学习笔记 - 数组篇
c语言·笔记·学习
何曾参静谧3 小时前
「C/C++」C++20 之 #include<ranges> 范围
c语言·c++·c++20
ChaoZiLL3 小时前
关于我的数据结构与算法——初阶第二篇(排序)
数据结构·算法
single5944 小时前
【综合算法学习】(第十篇)
java·数据结构·c++·vscode·学习·算法·leetcode
free_girl_fang5 小时前
夯实根基之MySql从入门到精通(一)
java·数据结构·数据库·mysql