栈和队列的相同点和区别(图文并茂,新手必看)

栈和队列是数据结构中的两种存储结构,它们的相同点是:

栈和队列存储的都是逻辑关系为“一对一”的数据,本质上它们都属于线性存储结构。

栈和队列的区别在于:

栈存储数据,讲究“先进后出”,即最先进栈的数据,最后出栈;队列存储数据,讲究 "先进先出",即最先进队列的数据,也最先出队列。

对于没有系统学过栈和队列的读者,上面两句话很可能读不明白。没关系,接下来我将详细讲解栈和队列这两种存储结构。

 

数据结构中的栈

 对于逻辑关系为“一对一”的数据,除了用顺序表链表存储外,还可以用栈结构存储。

栈是一种“特殊”的线性存储结构,它的特殊之处体现在以下两个地方:
1、元素进栈和出栈的操作只能从一端完成,另一端是封闭的,如下图所示:

栈存储结构示意图

图 1 栈存储结构示意图

通常,我们将元素进栈的过程简称为“入栈”、“进栈”或者“压栈”;将元素出栈的过程简称为“出栈”或者“弹栈”。

2、栈中无论存数据还是取数据,都必须遵循“先进后出”的原则,即最先入栈的元素最先出栈。以图 1 的栈为例,很容易可以看出是元素 1 最先入栈,然后依次是元素 2、3、4 入栈。在此基础上,如果想取出元素 1,根据“先进后出”的原则,必须先依次将元素 4、3、2 出栈,最后才能轮到元素 1 出栈。

我们习惯将栈的开口端称为栈顶,封口端称为栈底。例如在图 1 中,元素 4 一侧为栈顶,元素 1 一侧为栈底,如图 2 所示。

栈顶和栈底

图 2 栈顶和栈底

由此我们可以对栈存储结构下一个定义:栈一种“只能从一端存取元素,且存取过程必须遵循‘先进后出’原则”的线性存储结构。

栈的实际应用

对于刚刚接触栈存储结构的读者来说,可能很难理解为什么会设计出“栈”这种存储结构,它有什么用?栈是一种特殊的线性存储结构,借助它的“特殊性”,可以解决很多实际问题。

1) 实现浏览器的“回退”功能

所谓浏览器的“回退”功能,比如您用浏览器打开 A 页面,然后从 A 页面跳转到 B 页面,然后再从 B 页面跳转到 C 页面。这种情况下,如果想回到 A 页面,有两种方法:

  • 重新搜索找到 A 页面;
  • 借助浏览器的“回退”功能,先从 C 页面回退到 B 页面,再从 B 页面回退到 A 页面。

很多浏览器的“回退”功能就位于工具栏中,图标是一个类似的箭头。

浏览器的“回退”功能底层就是用栈存储结构实现的,当从 A 页面跳转到 B 页面时,浏览器会执行入栈操作,A 页面信息会存入栈中;同样,从 B 页面跳转到 C 页面时,B 页面信息会存入栈中。当点击浏览器的“回退”按钮时,浏览器会执行“出栈”操作,根据“先进后出”的原则,B 页面先出栈,然后 A 页面出栈,这样就实现了“回退”的功能。

2) 实现 C 语言函数的相互调用

C语言程序中,函数间的相互调用过程也是用栈存储结构实现的。

举个简单的例子:

/**
*  快速入门数据结构 https://xiecoding.cn/ds/
**/
void func(){
    printf("Hello,World!");
}
int main(){
    func();
    return 0;
}

这段 C 语言程序中有两个函数,分别是 main() 函数和 func() 函数,其中 main() 函数内部调用了 func() 函数。

程序执行时,main() 函数会最先入栈,执行到第 5 行代码时,需要执行 func() 函数,此时会将 func() 函数入栈。等待 func() 函数执行完后,func() 函数会出栈,此时 main() 函数中的剩余代码会继续执行,直至 main() 函数执行完毕,做出栈操作,整个程序就执行结束了。

3) 解决一些实际问题

借助栈存储结构,可以快速解决类似“进制转换”、“括号匹配”等问题,具体的解决过程会在后续文章中做详细讲解。

栈的具体实现

线性表类似,栈存储结构也有两种具体的实现方案:

  • 顺序栈:用顺序表存储数据,数据存取的过程严格遵循栈结构的规定;
  • 链栈:用链表存储数据,数据存储的过程严格遵循栈结构的规定。

显然,顺序栈和链栈两种实现方案,本质的区别仍然是顺序表和链表之间的区别,即顺序栈是将所有数据集中存储,而链栈是将数据分散存放,元素之间的逻辑关系靠指针维系。

数据结构中的队列 

队列用来存储逻辑关系为“一对一”的数据,是一种“特殊”的线性存储结构。

顺序表链表相比,队列的特殊性体现在以下两个方面:
1、元素只能从队列的一端进入,从另一端出去,如下图所示:

队列存储结构

图 1 队列存储结构

通常,我们将元素进入队列的一端称为“队尾”,进入队列的过程称为“入队”;将元素从队列中出去的一端称为“队头”,出队列的过程称为“出队”。

2、队列中各个元素的进出必须遵循“先进先出”的原则,即最先入队的元素必须最先出队。

以图 1 所示的队列为例,从各个元素在队列中的存储状态不难判定,元素 1 最先入队,然后是元素 2 入队,最后是元素 3 入队。如果此时想将元素 3 出队,根据“先进先出”原则,必须先将元素 1 和 2  依次出队,最后才能轮到元素 3 出队。

强调:和队列不要混淆,栈是一端开口、另一端封口,元素入栈和出栈遵循“先进后出”原则;队列是两端都开口,但元素只能从一端进,从另一端出,且进出队列遵循“先进先出”的原则。

队列的实际应用

队列在操作系统中应用的十分广泛,比如用它解决 CPU 资源的竞争问题。

对于一台计算机来说,CPU 通常只有 1 个,是非常重要的资源。如果在很短的时间内,有多个程序向操作系统申请使用 CPU,就会出现竞争 CPU 资源的现象。不同的操作系统,解决这一问题的方法是不一样的,有一种方法就用到了队列这种存储结构。

假设在某段时间里,有 A、B、C 三个程序向操作系统申请 CPU 资源,操作系统会根据它们的申请次序,将它们排成一个队列。根据“先进先出”原则,最先进队列的程序出队列,并获得 CPU 的使用权。待该程序执行完或者使用 CPU 一段时间后,操作系统会将 CPU 资源分配给下一个出队的程序,以此类推。如果该程序在获得 CPU 资源的时间段内没有执行完,则只能重新入队,等待操作系统再次将 CPU 资源分配给它。
 
队列还可以用来解决一些实际问题,比如实现一个简单的停车场管理系统、实现一个推小车卡牌游戏等,后续会做详细讲解。

队列的具体实现

和栈的实现方案一样,队列的实现也有两种方式,分别是:

  • 顺序队列:用顺序表模拟实现队列存储结构;
  • 链队列:用链表模拟实现队列存储结构。

两者的区别仅是顺序表和链表的区别,即顺序队列集中存储数据,而链队列分散存储数据,元素之间的逻辑关系靠指针维系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值