数据结构学习第四天---栈与队列的概念

1、概念

前面我们学习了链表!链表与顺序表统称为线性表! 那么线性表中的顺序表在物理上存储的层次来说的话应该是一组连续的内存空间存储数据,链表是将元素存放在通过链接构造起来的一系列存储块中。那么我们如何利用这些数据呢?那么我们就要使用栈了!

 栈(stack)是一种容器,可存放数据元素、访问元素、删除元素。它的特点是只能允许在容器的一端进行加入数据(push)和输出数据(pop)的运算。没有位置概念,它确保了在任何时候可以访问、删除的元素都是此前最后存入的那个元素,确定了一种默认的 访问顺序,(LIFO,Last In First Out)后进先出的原理运作

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。队列是一种先进先出(First in First out)的线性表,简称 是FIFO。允许插入的一端为队尾,允许删除的一端为对头。队列不允许在中间部分进行操作,排在第一个的优先出列,最后来的排在队伍最后。

2、栈的列表式实现

#coding=utf8

class Stack(object):
    """栈"""
    def __init__(self):
        self.__list = []
        """这里我们使用的顺序表来构建的栈的容器
            那么存在一个问题!就是我们是将顺序表的头部当成栈顶呢?还是把顺序表的尾部当成一个栈顶呢?
            我们一般是使用的尾部,因为操作头部,对于顺序表来说是件复杂度是O(n)。
            操作尾部的话我们的是件复杂度是O(1)。
            
            如果我们使用单链表的数据结构来当容器的话!我们一般使用单链表的头部作为我们的栈顶!
            原因很简单!跟顺序表正好相反!单链表操作头部的时候的时间复杂度是O(1),
            但是操作尾部的时候,时间复杂度是O(n)。
            
            这里还要注意一点!在讲到顺序表的O(n)和单链表的O(n)的时候,单链表的O(n)要比顺序表的O(n)效率更高,
            因为单链表的O(n)的操作主要是花在遍历上面!但是对于顺序表的O(n),是对之前的所有元素进行其他的操作了!不仅仅是读写了!
        """



    def push(self,item):
        """添加一个新的元素item到栈顶"""
        self.__list.append(item)
    def pop(self):
        """弹出站定的元素"""
        return self.__list.pop()
        pass
    def peek(self):
        """返回栈顶元素"""
        if self.__list:
            return self.__list[-1]
        else:
            return None
    def is_empty(self):
        """判断栈是否伟空"""
        return self.__list ==[]

    def size(self):
        """返回栈的元素个数"""
        return len(self.__list)

if __name__ == '__main__':
    s = Stack()
    s.push(1)
    s.push(2)
    s.push(3)
    s.push(4)
    print(s.pop())
    print(s.pop())
    print(s.pop())
    print(s.pop())

压进去的是1234 , 弹出来的是4321 , 很简单的就实现了! 这是因为我们采用了列表的这样的存储结构的二次开发!所以才显的很简单。

3、队列的实现 (双端队列和队列)

队列:队列在上面已经介绍过了,下面说一下双端队列! 

双端队列(deque,全名,double-ended queue):是一种具有队列和栈的性质的数据结构,双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。双端队列可以在队列任意一端入队和出队。

这是一个双端队列的表示。下面代码完成一个队列!和一个双端队列。

#coding=utf8
class Queue(object):
    """单向队列"""
    def __init__(self):
        """
        对于队列的实现,我们的存储结构也是可以使用顺序表或者是使用链表的。这里我们使用的顺序表!
        再用顺序表做存储结构的时候,我们需要考虑的一个问题是:
            我们是从头部添加元素,尾部弹出元素呢?
            还是我们从尾部添加元素,头部弹出元素呢?
        分析:
            当我们从头部添加元素的时候!时间复杂度是O(n),尾部弹出元素的时候,我们的时间复杂度是O(1)
            当我们从尾部添加元素的时候!时间复杂度是O(1),头部弹出元素的时候,我们的时间复杂度是O(n)
        结论:
            无论我们采用上面的哪两种方式,都会发现一个时间复杂度为O(1),一个时间复杂度为O(n).
            那么我们选择哪个会更好呢?这个时间我们就要根据我们的业务需求去思考,我们的这个队列是添加用的多呢?还是删除用的多!
            根据情况而定。

        """
        self.__list = []

    def enqueue(self,item):
        """往队列中添加一个item元素"""
        self.__list.append(item)
    def dequeue(self):
        """从队列头部删除一个元素"""
        return self.__list.pop(0)
    def is_empty(self):
        """判断一个队列是否为空"""
        return self.__list == []
    def size(self):
        """返回队列的大小"""
        return len(self.__list)


class Dqueue(object):
    """双端队列"""
    def __init__(self):
        self.__list = []

    def add_front(self,item):
        """往双端队列的列表存储结构的头部插入一个元素"""
        self.__list.insert(0,item)
    def add_rear(self,item):
        """往双端队列的列表存储结构的尾部插入一个元素"""
        self.__list.append(item)
    """上面的两种写法其实本质上的意思是说:我可以在双端队列的头部和尾部都插入数据"""
    def pop_front(self):
        """从双端队列的列表存储结构的头部删除一个元素"""
        return self.__list.pop(0)
    def pop_rear(self):
        """从双端队列的列表存储结构的尾部删除一个元素"""
        return self.__list.pop()
    """上面的两种写法的本质上的意思是说:我可以在双端队列的头部或者尾部删除元素"""
    def is_empty(self):
        """判断该双端队列是否为空"""
        return self.__list == []
    def size(self):
        """返回队列的大小"""
        return len(self.__list)

if __name__ =="__main__":
    q = Queue()
    q.enqueue(1)
    q.enqueue(2)
    q.enqueue(6)
    q.enqueue(4)
    print(q.dequeue())
    print(q.dequeue())
    print(q.dequeue())
    print(q.dequeue())

这上面就是关于一个队列的实现!但是要记住的是,队列的底层其实还是借助的链表或者列表来完成的!

猜你喜欢

转载自blog.csdn.net/weixin_41928342/article/details/85377181