[读书笔记]-大话数据结构-4-栈与队列(一)-栈、共享栈和链栈
栈与队列
栈是限定仅在表尾进行插入和删除操作的线性表。
队列是只允许在一端进行拆入操作、而另一端进行删除操作的线性表。
栈的定义
栈(stack)是限定仅在表尾进行插入和删除操作的线性表。我们把允许插入和删除的一端称为栈顶(top),另一端称为(bottom),不含任何元素的栈称为空栈。栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构。
栈的拆入操作,也叫做进栈,也称压栈、入栈。栈的删除操作,也叫做出栈,也有的叫做弹栈。
栈的抽象数据类型
ADT 栈(stack) Data '''同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。''' Operation InitStack(*S):'''初始化操作,建立一个空栈S。''' DestroyStack(*S):'''若栈存在,则销毁它''' ClearStack(*S):'''将栈清空''' StackEmpty(S):'''若栈为空,返回true,否则返回false''' GetTop(S,*e):'''若栈存在且非空,用e返回S的栈顶元素。''' Push(*S,e):'''若栈S存在,插入新元素e到栈S中并成为栈顶元素。''' Pop(*S,e):'''删除栈S中栈顶元素,并用e返回其值。''' StackLength(S):'''返回栈S的元素个数。''' endADT
栈的顺序存储结构及实现
我们需要一个线性表存储数据,和一个指向栈顶的top变量。下标0表示栈底。
class SqStack(object): def __init__(self,size=20): self.data=[None for i in range(size)] self.size=size self.top=-1
一个大小为5的栈如下图所示:
栈的顺序存储结构--进栈操作
对于进栈操作,Python代码如下:
def Push(self,e): #将e压入栈顶 if self.top+1>=self.size: #满栈 return 0 #压栈失败 self.top+=1 #栈顶指针+1 self.data[self.top]=e #将新元素赋值给栈顶 return 1 #压栈成功
栈的顺序存储结构--出栈操作
def Pop(self): #若栈不为空,讲栈顶元素,否则返回None if self.top<0: #若栈为空,返回None return None e=self.data[self.top] #获得栈顶元素 self.data[self.top]=None #删除栈顶元素 self.top-=1 #栈指针-1 return e
栈的线性存储结构完整代码
class SqStack(object): def __init__(self,size=20): self.data=[None for i in range(size)] self.size=size self.top=-1 def Push(self,e): #将e压入栈顶 if self.top+1>=self.size: #满栈 return 0 #压栈失败 self.top+=1 #栈顶指针+1 self.data[self.top]=e #将新元素赋值给栈顶 return 1 #压栈成功 def Pop(self): #若栈不为空,讲栈顶元素,否则返回None if self.top<0: #若栈为空,返回None return None e=self.data[self.top] #获得栈顶元素 self.data[self.top]=None #删除栈顶元素 self.top-=1 #栈指针-1 return e def ClearStack(self): #将栈清空 while self.top>=0: self.data[self.top]=None self.top-=1 def StackEmpty(self): #判断栈是否为空 return self.top==-1 def GetTop(self): #返回栈顶元素 return self.data[self.top] def StackLength(self):#返回栈的元素个数 return self.top+1
两栈共享空间
用线性存储表示栈有好处,只需要再一端操作,避免了插入、删除移动大量元素,但也有一个缺点,事先不知道栈的大小,如果开辟太多空间,造成存储空间浪费。因此可以用两个占共享空间,即一个栈的栈底为数组的始端(下标为0),另一个栈为数组的末端(下标n-1),这样两个栈如果增加元素就从两端往中间延伸。如下图:
class SqDoubleStack(object): def __init__(self,size=40): self.data=[None for i in range(size)] #列表数据 self.size=size #栈的大小 self.top1=-1 #第一个栈为空时指向-1 self.top2=size #第二个栈为空时指向n
两栈共享空间的push方法
对第一个栈压栈的时候,其top指针+1;第二个栈压栈时,top指针-1,当两个指针相遇时,表示栈满。
def Push(self,e,stacknumber): if self.top1+1==self.top2: #栈满 return 0 if stacknumber==1: #如果为第一个栈 self.top1+=1 #栈顶+1 self.data[self.top1] =e #给栈顶位置赋值 elif stacknumber==2: #如果为第二个栈 self.top2-=1 #栈顶-1 self.data[self.top2] =e #给栈顶赋值 else: return 0 return 1
两栈共享空间的pop方法
与压栈类似,第一个栈出栈时,top指针-1,当其指向-1时表示空栈;第二个栈出栈时top指针+1,当其指向n时,表示满栈。
def Pop(self,stacknumber): if stacknumber==1: if self.top1<0: #如果栈1为空,返回0 return 0 e=self.data[self.top1] #取出栈1的元素 self.data[self.top1]=None self.top1-=1 elif stacknumber==2: # if self.top2>=self.size: #如果栈2为空,返回0 return 0 e=self.data[self.top2] #取出栈2的元素 self.data[self.top2]=None self.top2+=1 else:return 0 #如果栈指定的不对,返回0 return e #返回e
事实上,这样的数据结构,通常是两个栈的空间需求有相反关系时,采用较多。
两栈共享空间的完整代码
class SqDoubleStack(object): def __init__(self,size=40): self.data=[None for i in range(size)] #列表数据 self.size=size #栈的大小 self.top1=-1 #第一个栈为空时指向-1 self.top2=size #第二个栈为空时指向n def Push(self,e,stacknumber): if self.top1+1==self.top2: #栈满 return 0 if stacknumber==1: #如果为第一个栈 self.top1+=1 #栈顶+1 self.data[self.top1] =e #给栈顶位置赋值 elif stacknumber==2: #如果为第二个栈 self.top2-=1 #栈顶-1 self.data[self.top2] =e #给栈顶赋值 else: return 0 return 1 def Pop(self,stacknumber): if stacknumber==1: if self.top1<0: #如果栈1为空,返回0 return 0 e=self.data[self.top1] #取出栈1的元素 self.data[self.top1]=None self.top1-=1 elif stacknumber==2: # if self.top2>=self.size: #如果栈2为空,返回0 return 0 e=self.data[self.top2] #取出栈2的元素 self.data[self.top2]=None self.top2+=1 else:return 0 #如果栈指定的不对,返回0 return e #返回e def ClearStack(self,stacknumber):#清空某个栈 if stacknumber==1: while self.top1>=0: #当栈1部委空时,循环一下操作 self.data[self.top1]=None #清除栈1的当前元素 self.top1-=1 #栈1的栈顶位置往下移 elif stacknumber==2: while self.top2<self.size: #当栈2不为空时,循环 self.data[self.top2]=None #清除当前 self.top2+=1 #栈2的栈顶往上移 else: raise ERROR() def StackEmpty(self,stacknumber):#某个栈是否为空 if stacknumber==1: return self.top1==-1 #返回栈1是否为空 elif stacknumber==2: return self.top2==self.size #返回栈2是否为空 else:raise ERROR() def GetTop(self,stacknumber):#获取某个栈的栈顶元素 if stacknumber==1: if self.top1>=0: return self.data[self.top1] #栈1不为空返回栈顶元素 elif stacknumber==2: if self.top2<=self.size: return self.data[self.top2] #栈2不为空返回栈顶元素 return None #否则返回空 def StackLength(self,stacknumber):#返回栈的长度 if stacknumber==1: return self.top1+1 elif stacknumber==2: return self.size-self.top2 else:raise ERROR()
栈的链式存储结构及实现
栈的链式存储结构,简称链栈。对于链式存储结构,最好的方法是把栈顶放在链表的头部,链栈基本不存在栈满的情况,除非内存满。链栈的top指针指向空时,表示空栈。链栈的示意图如下:
链栈的实现代码
class Node(object): #定义链表节点 def __init__(self,data=None): self.data=data self.next=None class LinkStack(object): def __init__(self): #生成一个空的链栈 self.top=None self.count=0 #计算栈的元素个数
链栈的进栈操作
def Push(self,e): #将e压入栈顶 tnode=Node(e) #新生成一个节点 tnode.next=self.top #指向栈顶的元素 self.top=tnode #栈顶指向这个新节点 self.count+=1
链栈的出栈操作
def Pop(self): #若栈不为空,讲栈顶元素,否则返回None if self.top==None: #如果栈空,返回None return None else: e=self.top #获取栈顶元素 self.top=self.top.next #栈顶指向下一个元素 self.count-=1 return e.data
链栈的完整代码
class LinkStack(object): def __init__(self): #生成一个空的链栈 self.top=None self.count=0 #计算栈的元素个数 def Push(self,e): #将e压入栈顶 tnode=Node(e) #新生成一个节点 tnode.next=self.top #指向栈顶的元素 self.top=tnode #栈顶指向这个新节点 self.count+=1 def Pop(self): #若栈不为空,讲栈顶元素,否则返回None if self.top==None: #如果栈空,返回None return None else: e=self.top #获取栈顶元素 self.top=self.top.next #栈顶指向下一个元素 self.count-=1 return e.data def ClearStack(self): #将栈清空 self.top =None self.count=0 def StackEmpty(self): #判断栈是否为空 return self.top==None def GetTop(self): #返回栈顶元素 return self.top.data def StackLength(self):#返回栈的元素个数 return self.count
顺序栈与链栈的时间复杂度都是O(1),但顺序栈需要事先确定一个长度,可能存在空间浪费问题。
如果栈的使用过程中元素的变化不可预料,有事很小,有时非常大,那么最好用链栈,反之,如果它的变化在可控范围内,建议用顺序栈会好一些。