1.栈和堆的基本概念
栈和队列都是操作受限制的线性表。由于都是线性表,后面也会介绍它们的实现都有顺序和链式两种结构;由于操作受到限制,因此它们各自有各自的特点。
1.1栈的特点
栈是一种只能在一端进行插入或删除操作的线性表,这一端被称为栈顶,相对地,把另一端称为栈底。它的主要特点就是先进先出(FILO—First-In/Last-Out),如图所示。它的原理好比开进一个死胡同的多辆汽车,最先开进去的汽车只能最后出来。
1.2队列的特点
队列式一钟允许在表的一端进行插入,而在表的另一端进行删除的线性表,它的特点概括起来就是先进先出(FIFO—first in first out)。就如它的名字一样,原理好比排队,最先进入队列的人总是第一个出队。
2.数据结构和算法
2.1栈的数据结构和算法
栈的数据结构主要有顺序栈和链栈。最常用的应该是顺序栈。
顺序栈可以简单的用数组表示。定义如下
typedef struct
{
int data[maxSize];
int top;
}SqStack;
对于顺序栈St的两个状态和两个操作。
栈空St.top==-1
栈满St.top==maxSize-1
进栈++St.top;St.data[St.top]=x;
出栈x=St.data[St.top];--St.top;
当然上面只是举例,不同的情况可以做相应的改变。其实更为常见的情况应该是对于不同类型的数据,new相应的数组,然后用指针来指示栈顶。
栈的另一种实现链栈,链栈的结构与链表是一样的。进栈相当于将元素插入到头结点之后(头插法),而出栈相当于将头结点之后的那个元素删除。这里不再赘述。
2.2队列的数据结构和算法
队列和栈一样,有顺序队列和链队。
顺序队列我感觉用处不大,毕竟太浪费空间。更加有用的应该是循环队列(当然循环队列也有数组实现和链表实现,我们这里主要讲数组的实现)。
循环队列示意图如下。
就像右图所示它其实就是一个数组,但入栈和出栈的方式让指示器(front和rear)一直在这个数组范围内移动(从0递增7再变为0递增,一直循环下去),这样就不会像顺序队列一样空间被浪费。
循环队列中,由于入队时尾指针向前追赶头指针;出队时头指针向前追赶尾指针,造成队空和队满时头尾指针均相等。因此,无法通过条件front==rear来判别队列是”空”还是”满”。
因此我们设置
队空状态:rear==front。
队满状态:(rear+1)%maxSize==front(其实并未满,损失了一个空间)
进队操作:rear=(rear+1)%maxSzie;data[rear]=x;
出队操作:front=(front+1)%maxSize;x=data[front];
这里我们先移动指针再存取元素,当然也可以先存取元素在移动指针。
队列的另一种实现是链队。结构和链表一样。只是有两个指针指向链头(链表中的第一个元素)和链尾(链表中的最后一个元素),如下图所示。队空时两个指针均为null,进队时链尾指针操作,相当于从链表的最后插入元素(要单独考虑没有元素的时候),出队就是删除第一个元素(要单独考虑只有一个元素的时候和没有元素的时候)。由于时间原因,具体过程不再赘述。
3.相关应用
3.1栈的应用(输出八进制数)
栈的应用有很多,包括大到语言的函数调用和递归等实现,小到表达式求解等应用。
归纳一下,如果一个问题中出现这种情况:
即在解决问题的过程中出现了一个状态,但凭借现有条件不能判断当前的状态是否可以解决,需要记下等待以后出现可以解决当前状态的条件后返回来解决。这种问题需要用到栈来解决,栈具有记忆功能。
这里用一个比较简单的程序八进制输出(输入一个十进制数,输出其八进制数)来演示,用到栈是C++ STL中stack。
#include<iostream>
#include<stdio.h>
#include<stack>
#include<stdlib.h>
using namespace std;
int main()
{
stack <int> s;
int num;
while(scanf("%d",&num)!=EOF)
{
while(num)
{
s.push((num%8));
num=num/8;
}
while(!s.empty())
{
cout<<s.top();
s.pop();
}
cout<<endl;
}
return 0;
}
3.2队列的应用(猴子选大王)
n只猴子围坐成一个圈,按顺时针方向从1到n编号。然后从1号猴子开始沿顺时针方向从1开始报数,报到m的猴子出局,再从刚出局猴子的下一个位置重新开始报数,如此重复,直至剩下一个猴子,它就是大王。设计并编写程序,实现如下功能:
(1) 要求由用户输入开始时的猴子数n、报数的最后一个数m。
(2) 给出当选猴王的初始编号。
这道题(第一次碰到这题是一场在保研的机试上,当时竟然不会做)放在这边不知道是不是合适,因为它并没有用到队列的出队入队,但它很好的解释了循环队列的思想。
代码如下:
#include <iostream>
using namespace std;
int main()
{
int n=0, m=0;
cin >> n >> m;
int x = n; //x记录猴子的个数
int *A = new int[n];
int i = 0;
for (i = 0; i<n;i++) //设置好编号
{
A[i] = i + 1;
}
i = 0;
int count = 0; //计数器,从1-m
while (x > 1) //是否只剩下一只猴子
{
if (A[i]==-1) //判断当前的猴子是否淘汰出局
{
i = (i + 1) % n; //已经淘汰出局直接走向下一只
continue;
}
else
{
if (count==m-1) //若正好报道m,(由于从0开始所以要减1)
{
A[i] = -1; //该猴子淘汰
i = (i + 1) % n; //到下一个猴子
count = 0; //计数器置为0
x--; //猴子数量减少一只
}
else //报道的不是m,计数器+1,猴子到下一只
{
count++;
i = (i + 1) % n;
}
}
}
for (i = 0; i < n;i++) //遍历,不是-1的猴子就是最后留下来的猴子
{
if (A[i]!=-1)
{
cout << A[i] << endl;
}
}
return 0;
}
4.总结
栈和队列还是比较简单的,特别是在实现上(这也让我在写的时候不是很情愿,因为感觉太简单,没有必要要写,而且实现不管是java还是C++都有相应的系统库)。但应用范围很广,比如毕设做的Java虚拟机,栈可是其中很重要的数据结构。最重要的是理解栈和队列的思想,并学会如何应用。
本文详细介绍了栈和队列的基本概念,包括它们的特点、数据结构和算法实现。栈是后进先出(LIFO)的数据结构,常用于表达式求解、递归等;而队列遵循先进先出(FIFO)原则,常见应用有任务调度、缓冲区等。文中还通过八进制转换和猴子选大王问题展示了栈和队列的实际应用。
&spm=1001.2101.3001.5002&articleId=51798709&d=1&t=3&u=c67476cbfad14ac8829eb80b64fd71ec)
1354

被折叠的 条评论
为什么被折叠?



