栈和队列修炼指南(基本操作+OJ练习)

简介: 栈和队列修炼指南(基本操作+OJ练习)

栈和队列修炼指南

1. 栈

1. 1 概念及结构

  • 栈:是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作。进行数据插入和删除操作的一端称为栈顶,另一端为栈底
  • 栈中的数据元素遵守后进先出原则(LIFO)原则
  • 压栈:栈的插入操作称为进栈/压栈/入栈,其位置在栈顶
  • 出栈:栈的删除操作称为出栈,其位置也在栈顶

1.2 分类(数组栈和链式栈)

数组栈(推荐方式,因为在数组尾插代价更小)

链式栈:相较数组栈无优势,且一般将链表尾作为栈底,链表头作为栈顶(单链表情况下)

1.3 数组栈

1.3.1 结构的定义

typedef int STElemType;
typedef struct Stack
{
  STElemType *data; //动态栈
  int top;
  int capacity;
}ST;

1.3.2 初始化

void StackInit(ST *pt)
{
  pt->data = (SElemType *)malloc(N*sizeof(SElemTyp  e));
    if (!pt->data)
    {
      perror("malloc");
      exit(1);
  }
  pt->capacity = N; //N表示初始的最大容量
  pt->top = 0;  //此时top指向的是栈顶元素的下一个位置,也可以定义为pt->top=-1,这样,top就是指向栈顶元素
}

1.3.3 销毁

void StackDestroy(ST *pt)
{
  free(pt->data);
  pt->top=pt->capacity=0;
}

1.3.4 判断栈是否为空

bool StackEmpty(ST *pt)
{
  return pt->top==0;  //若为真即栈为空,则返回1,否则返回0
}

1.3.5入栈

void StackPush(ST *pt,SElemType x)
{
  if(pt->top==pt->capacity)     //如果容量已满
  {
    pt->capacity *= 2;    //将容量扩为原来的两倍
    ST* temp = realloc(pt->data, pt->capacity*sizeof(SElemType));
    if(!temp)
    {
      perror("malloc");
      exit(1);
    }
        pt->data = temp;
  }
    //入栈
  pt->data[pt->top]=x;
  pt->top++;
}

1.3.6 出栈

void StackPop(ST *pt) 
{
  assert(!stackEmpty(pt));  //栈不能为空
    //出栈
  pt->top--;
}

1.3.7 返回栈顶元素

SElemType StackTop(ST *pt)
{
  assert(!stackEmpty(pt));  //栈不能为空
  return pt->data[pt->top-1]; 
}

1.3.8 返回栈的元素个数

int StackSize(ST *pt)
{
  return pt->top;
}

1.3.9 将栈的元素全部取出

void StackPrint(ST *pt)
{
  assert(!stackEmpty(pt));  //栈不能为空
  while(!StackEmpty(pt))
  {
    printf("%d ",StackTop(pt)); //遵循先入后出原则,从上往下取
    pt->top--;
  }
}

1.4 练习

学习完栈的基本概念和相关操作后,你可以利用栈的特性做下面的OJ题:

有效括号序列👉题目解析

逆波兰表达式求值👉题目解析

删除字符串中的所有相邻重复项👉题目解析

包含min函数的栈👉题目解析


2. 队列

2.1 概念及结构

  • 队列:只允许在一端进行入数据操作,在另一端进行删除数据操作的特殊线性表
  • 遵循先进先出的原则FIFO
  • 入队列:进行插入操作的一段叫做队尾
  • 出队列:进行删除操作的一段叫做队头

2.2. 分类(数组队列和链队列)

数组队列:由于出队列出的是队头元素,因此数组队列出数据的效率低下,不推荐使用

链队列:入和出数据的效率都很高,是队列常用的表示法

2.3 链队列

2.3.1 结构的定义

typedef int QDataType;  //存储的数据类型
typedef struct QueueNode  //链队列的节点
{
  struct QueueNode *next;
  QDataType data;
}QueueNode;
typedef struct Queue  //定义存放指向队头,队尾指针的结构体
{
  QueueNode *head;  //指向队头
  QueueNode *tail;  //指向队尾
}Queue;

2.3.2 初始化

void QueueInit(Queue *pq)
{
  assert(pq);
  pq->head = NULL;
  pq->tail = NULL;
}

2.3.3 销毁

void QueueDestroy(Queue *pq)
{
  QueueNode *cur = pq->head;  //定义临时变量
  while (cur)
  {  
    pq->head = pq->head->next;  //链表下滑
    free(cur);    //释放空间
    cur = pq->head; //更新临时变量
  } 
  pq->tail = NULL;  //空间释放完毕后head已经为空,但tail成为了野指针,所以要置空
}

2.3.4 判断队列是否为空

bool QueueEmpty(Queue *pq) 
{
  assert(pq);
  return pq->head == NULL;
}

2.3.4 入队

void QueuePush(Queue *pq,QDataType x)
{
  assert(pq);
  QueueNode *newnode=(QueueNode *)malloc(sizeof(QueueNode));    //创建新节点
    if (NULL == newNode)
    {
        perror("malloc");
        exit(1);
  }
  newnode->data=x;
  newnode->next=NULL;
  if(QueueEmpty(pq))  //如果队列为空
  {
    pq->head=newnode; //使队头、队尾指针同时指向新节点
    pq->tail=newnode;
  }
  else
  {
    pq->tail->next=newnode; //使队尾指针的指向下一个节点的指针指向新节点
    pq->tail=newnode; //更新队尾指针
  }
}

2.3.5 出队

void QueuePop(Queue *pq) 
{
  assert(pq);
  assert(!QueueEmpty(pq));  //队列不能为空
  QueueNode *cur=pq->head;  //定义临时变量保存队头指针
  pq->head=pq->head->next;  //使队头指针指向下一个节点
  free(cur);    //释放原来的队头
  if(pq->head==NULL)
    pq->tail=NULL;  //如果节点已经全部出队,则要将队尾指针置空,防止形成野指针
}

2.3.6 返回队头/队尾数据域

//返回队头元素
QDataType QueueFront(Queue *pq)  
{
  assert(pq);
  assert(!QueueEmpty(pq));
  return pq->head->data;
}
//返回队尾元素 
QDataType QueueBack(Queue *pq)
{
    assert(pq);
    assert(!QueueEmpty(pq));
    return pq->tail->data;
}

2.3.7 返回队列元素个数

int QueueSize(Queue *pq)
{
  QueueNode *cur=pq->head;
  int size=0;
  while(cur)
  {
    size++;
    cur=cur->next;
  }
  return size;
}
//也可以在队列结构体中增加size变量,每入队一个size就加一

2.4 练习

队列常常被用来对一些复杂数据结构的广度优先遍历,但由于目前还未学习,故不作深入讨论

除了这种最基本的只能从队尾插入数据,从队头删除数据的队列外,其实还有循环队列、双端队列、单调队列等许多复杂但功能强大的队列结构,如果小伙伴们感兴趣,也可以看看:

👉循环队列

👉双端队列 & 单调队列

如果小伙伴们愿意挑战,也可以做一做滑动窗口的最大值👉题目解析


3. 栈和队列的相互表示

这里拿两道OJ题来进行说明:

用两个栈表示队列👉题目解析

用两个队列表示栈👉题目解析

目录
打赏
0
0
0
0
4
分享
相关文章
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
106 1
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
21 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
5月前
|
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
77 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
8月前
|
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
746 9
|
8月前
|
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
183 58
|
6月前
|
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
299 77
|
6月前
|
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
217 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
6月前
|
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
109 9
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问