😉博客主页:倒霉沙拉🥗 💘欢迎关注👍评论🏆关注?? 🏃一起坚持,一起成长! ??如果文章中有错误或者大家有更好的想法,可以评论指出,谢谢!
0.前方有言
学习一个数据结构的顺序: 逻辑结构——存储结构——运算与操作 按照这样的顺序在大脑里构建思维导图会加深记忆哈!
1.栈的基本概念
1.1什么是栈?
栈是一种只能在同一端进行插入或删除操作的线性表(也称线性多项集)。 从概念中我们不难看出,栈其实就是一种特殊的线性表! 那么它特殊在哪里以至于我们可以单独拿出来作为一种新的数据结构来讨论呢?
1.2栈何以立于不败之地?
下面我们用生活中的例子来进入栈:
上图是一些书堆成的栈,这样堆起来的话只有最上面的Python会露出封面,当我们想要拿到其他的书的时候,我们就需要一本一本移除压在其上面的书,这样生活中不起眼的一些行为便包含着栈的主要特点,即“后进先出”或者“先进后出”,也就是说后进栈的元素先出栈,栈也因此被称为后进先出表。
当然,除了这个最重要的特点之外,栈还有一些专业领域需要我们去了解:
栈中允许进行插入、删除操作的一端称为栈顶,那么另一端就称为栈底了; 栈中没有数据元素时称为空栈(此时披着线性表的外衣,也就难以分辨是栈还是它大哥了); 栈的插入操作通常称为进栈、入栈或者是推入; 栈的删除操作通常称为退栈、出栈或者是弹出。
在以上都了解之后,那么对于栈来说,我们算是入门了,但是想要在栈的道路上继续耕耘,我们任重道远,还是需要继续往下看的;
2.栈的存储结构与基本运算
既然我们谈到栈是一种特殊的线性表,那么谈到栈的存储结构的时候,我们是不是就会联想到线性表的存储结构呢? 如果可以,还是需要继续看的;
2.1栈的顺序存储结构
在采用顺序存储结构时,用列表来存放栈中元素的栈就是顺序栈 由图我们可以总结出来顺序栈的四特点: 1)栈空条件:len(data)==0; 2)栈满条件:在Python中,列表是可以动态扩展的,所以我们不需要考虑栈满; 3)进栈操作:从上图可以看到使用了push( )的操作将元素添加在栈顶处; 4)出栈操作:使用了pop( )方法删除栈顶元素并返回该元素(有返回值的,这个要注意!)。
2.2顺序栈为我所用
学习的目的是为了应用,那么怎样才能让顺序栈为我们所用呢? 上面是对栈的一些操作的汇总,接下来我们来详细看一下栈的操作的具体实现吧(附代码):
1.判断栈是否为空:
def isEmpty(self):
if len(self.data)==0:
return True
return False
2.进栈:
def push(self,e):
self.data.append(e)
采用了列表的append方法将元素添加在栈顶。
3.出栈:
def pop(self):
return self.data.pop()
4.取栈顶元素:
def peek(self):
return self.data[-1]
2.3栈的链式存储结构
采用链式存储的栈称为链栈。 类比顺序栈的四特点,我们可以得到链栈的: 1)栈空条件:head.next==None ; 2)栈满条件:不需要考虑栈满上溢出,这同样是链栈的一个优点; 3)进栈操作:使用push( )方法; 4)出栈操作:使用pop( )方法。
2.4怎样掌控链栈?
工欲善其事必先利其器; 想要熟练使用链栈,我们得先了解链栈的操作: 别跑神,看我操作!
1.判断栈是否为空:
def isEmpty(self):
if self.head.next==None:
return True
return False
2.进栈:
def push(self,e):
p=LinkNode(e)
p.next=self.head.next
self.head.next=p
3.出栈:
def pop(self):
p=self.head.next
self.head.next=p.next
return p.data
4.取栈顶元素:
def peek(self):
return self.head.next.data
3.栈的一些综合应用
1.回溯算法求解迷宫问题
一讲到迷宫,相信大家小时候包括现在都可能玩过,包括我们现在在最强大脑中见到过的蜂巢迷宫等;
其实迷宫也是有很长的历史了,迷宫最初是在克里特岛的克诺索斯宫出现,这里是米诺斯文明的中心。克诺索斯宫是政治与仪式的中心,以房间、起居空间、室内外的精美壁画以及具有高度装饰性的陶器组成的迷宫闻名。
回到我们今天的主题,借助栈,采用回溯算法求解迷宫问题;
回溯算法是什么呢?
拿我们自己玩迷宫的经历,当我们走到不通的路时,我们会返回重新选择另一个路口,回溯算法的设计便是出自于这样的考虑,计算机在求解迷宫问题时,采用的是遍历方法,通过对迷宫的各个路口遍历,遇到不通的路时,就像栈的操作一样,把走过的给移除,重新选择另一个路口,每次遍历都将路线加入在栈中,最终从入口到出口便会形成一个路径栈;这便是我们这个问题的设计思路。
我们该怎样取表示迷宫呢?
从图中我们可以看出来,迷宫的构成是由一个个方块构成的,这样的我们我们便可以建立一个二维列表,用0表示白块(即通路),用1表示黑块(即围墙),这样的话因为入口和出口都是确定的,我们便可以将其看作围墙,由此可以将迷宫表示成:
maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 1, 0, 1, 1, 1],
[1, 0, 1, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
怎样判断所走的方向?
当我们解决了上面的问题之后,我们就要开始考虑在任何一个位置的时候我们该选择哪一个方向走呢?其实要考虑这个就已经涉及到了路线的设计了,但是我们要清楚的是只有标记为0的位置才是可以走的,那我们在上下左右四个相邻方块中我们该怎样选择呢?这是我们第二步需要解决的问题;这样的话就要涉及到一个偏移量的问题,我们首先需要对方块进行编号; 按照左上角为0,横着0~9为x;纵向0-9为y;按照顺时针方向递增编号,这样我们可以得到x,y的偏移量为:
dx=[-1,0,1,0]
dy=[0,1,0,-1]
怎样走出迷宫?
在前面的两个问题都搞明白之后,我们就要开始设计走出迷宫的算法啦; 在正式设计之前,我们需要先建立一个栈类与一个方位类;
class SqStack:
def __init__(self):
self.data=[]
def empty(self):
if len(self.data)==0:
return True
return False
def push(self,e):
self.data.append(e)
def pop(self):
assert not self.empty()
return self.data.pop()
def gettop(self):
assert not self.empty()
return self.data[-1]
class Box:
def __init__(self,i1,j1,di1):
self.i=i1
self.j=j1
self.di=di1
这些类的建立方法,在上面的文章中都有涉及到; 之后就到最重要的部分了,我们需要从入口到出口(均以给定),我们就需要将当前方块的位置坐标放入栈中,依次遍历四个方向找到正确路径,如果走至错误路口,便根据栈的特点移除栈顶元素探索另一条路径; 代码如下:
def mazepath(xi,yi,xe,ye):
global maze
maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 1, 0, 1, 1, 1],
[1, 0, 1, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
st=SqStack()
dx=[-1,0,1,0]
dy=[0,1,0,-1]
e=Box(xi,yi,-1)
st.push(e)
maze[xi][yi]=-1
while not st.empty():
b=st.gettop()
if b.i==xe and b.j==ye:
for k in range(len(st.data)):
print("["+str(st.data[k].i)+','+str(st.data[k].j)+"]""\n",end=' ')
return True
find=False
di=b.di
while di<3 and find==False:
di+=1
i,j=b.i+dx[di],b.j+dy[di]
if maze[i][j]==0:
find=True
if find:
b.di=di
b1=Box(i,j,-1)
st.push(b1)
maze[i][j]=-1
else:
maze[b.i][b.j]=0
st.pop()
return False
xi,yi=1,6
xe,ye=8,8
print("迷宫路径为:",end=' ')
if not mazepath(xi,yi,xe,ye):
print("该迷宫无解")
最后将上述代码拼接在一起,便可以得到求解的路径了!
4.写在最后
到栈啦! 一个课间的时间便可以掌握了数据结构栈的全部基础,有时间的话还可以看一看迷宫的题目,考虑到文章篇幅与冗杂度问题,文章中没有放入过多眼花缭乱的图片,题目也只放入了一道综合应用的;但是呢,此非终版,我也会根据不同阶段的理解对文章进行改善,也会陆陆续续添加入一些新的有趣的题目!
对你有用的话,点个赞再走吧! 有建议的话,可以评论区轰炸我!
|