python实现的==双端队列==双端循环队列

什么是双端队列?

在这里插入图片描述
双端队列(Double-ended Queue,简称Deque)是一种具有队列和栈特性的数据结构,允许在队列的两端进行插入和删除操作。双端队列可以在队首和队尾同时进行入队和出队操作,因此可以用来实现更灵活的数据操作。

在这里插入图片描述


双端队列的链表实现(顺序表实现过于简单仅附源码)

在本篇博客中,我们将深入探讨双端队列的链表实现。双端队列是一种特殊的数据结构,允许在队首和队尾进行插入和删除操作。我将逐步介绍每个部分的功能和实现方法,并在代码中演示这些概念。

1.实现的所有的方法

方法名用途
is_empty()检查双端队列是否为空
length()获取双端队列的长度
items()返回包含队列中所有元素的列表
push(data)在队尾添加元素
push_left(data)在队首添加元素
pop()从队首弹出元素
pop_right()从队尾弹出元素
pop_right2()从队尾弹出元素的另一种实现
peek()查看队首元素

2.实现前的准备

在开始之前,我们需要首先定义一个节点类,用于构建链表的节点。每个节点将包含数据以及指向下一个节点的引用。

class Node():
    def __init__(self, data, next=None):
        self.data = data
        self.next = next

3.Deque类的基本结构

我们将使用Deque类来实现双端队列。这个类将包含队首和队尾节点的引用,以及一个用于记录队列长度的私有属性。

在这里插入图片描述

class Deque:
    def __init__(self):
        self.head = None
        self.rear = None
        self._length = 0

4.队列是否为空

我们首先需要一个方法来检查双端队列是否为空。

    def is_empty(self):
        return self._length == 0

5.获取队列长度

双端队列的长度是指队列中当前存储的元素数量。

    def length(self):
        return self._length

6.获取队列中的元素

我们可以编写一个方法来返回队列中所有元素的列表。
代码实现思路:

  1. 首先,我们创建一个空列表 res,用于存储队列中的元素。
  2. 接着,我们将当前节点指针 cur 设置为队首节点,这样我们可以从队首开始遍历链表。
  3. 我们使用一个循环来遍历队列中的所有节点。在每次循环中,我们将当前节点的数据添加到列表 res 中。
  4. 然后,我们将当前节点指针 cur 移动到下一个节点,以便继续遍历。
  5. 循环继续,直到所有节点都被遍历完毕。
  6. 最后,我们返回存储了队列元素的列表 res,这个列表包含了队列中的所有数据。
    def items(self):
        res = []
        cur = self.head
        while cur:
            res.append(cur.data)
            cur = cur.next
        return res

7.向队尾添加元素

要向双端队列的队尾添加元素,我们将新节点链接到当前队尾节点,并更新队尾引用。
在这里插入图片描述

代码具体实现思路:

  1. 首先,我们创建一个新的节点 node,将传入的数据 data 存储在节点中。
  2. 接着,我们检查双端队列是否为空。如果为空,说明这是队列中的第一个节点,我们将 node 设置为队首节点和队尾节点。
  3. 如果队列不为空,我们将当前队尾节点的 next 引用链接到新节点 node,然后将 node 设置为新的队尾节点。
  4. 最后,我们增加队列的长度计数 _length

代码如下:

    def push(self, data):
        node = Node(data)
        if self.is_empty():
            self.head = node
            self.rear = node
        else:
            self.rear.next = node
            self.rear = node
        self._length += 1

8.向队首添加元素

向队首添加元素需要更新新节点的next引用以链接到当前头节点,并更新队首引用。在这里插入图片描述

代码具体实现思路:

  1. 首先,我们创建一个新的节点 node,将传入的数据 data 存储在节点中。
  2. 然后,我们检查双端队列是否为空,如果为空,说明这是队列中的第一个节点,我们将 node 设置为队首节点和队尾节点。
  3. 如果队列不为空,我们将 nodenext 引用链接到当前队首节点,然后将 node 设置为新的队首节点。
  4. 最后,我们增加队列的长度计数 _length

代码如下:

    def push_left(self, data):
        node = Node(data)
        if self.is_empty():
            self.head = node
            self.rear = node
        else:
            node.next = self.head
            self.head = node
        self._length += 1

9.从队首弹出元素

从队首弹出元素意味着移除并返回队列中的第一个元素。
在这里插入图片描述


在这里插入图片描述

代码具体实现思路:

  1. 首先,我们检查双端队列是否为空。如果为空,抛出一个值错误,表示双端队列为空,不能执行弹出操作。
  2. 如果队列不为空,我们将队首节点 self.head 保存在变量 res 中,这将是我们要弹出的节点。
  3. 然后,我们更新队首节点为当前队首节点的下一个节点,以将它从队列中移除。
  4. 我们同时减少队列的长度计数 _length
  5. 接下来,我们检查队首节点是否为空。如果队首节点为空,说明队列已经为空,我们还需要将队尾节点 self.rear 设置为 None,以保持队列的一致性。
  6. 最后,我们返回变量 res 中保存的节点的数据,即完成了从队首弹出元素的操作。

代码实现如下:

    def pop(self):
        if self.is_empty():
            raise ValueError('双端队列为空')
        else:
            res = self.head
            self.head = self.head.next
            self._length -= 1
            if self.head is None:
                self.rear = None
            return res.data

10.从队尾弹出元素

从队尾弹出元素需要处理不同情况,包括只有一个节点和多个节点。

在这里插入图片描述


在这里插入图片描述


代码具体实现思路:

  1. 首先,我们检查双端队列是否为空。如果为空,抛出一个值错误,表示双端队列为空,不能执行弹出操作。
  2. 接着,我们检查队列是否只有一个节点。如果是,说明队列中只有一个节点,我们将队首节点 self.head 设置为 None,同时将队尾节点 self.rear 也设置为 None。
  3. 如果队列不为空且不止一个节点,我们先保存当前的队首节点 self.head 到变量 res 中,这将是我们要弹出的节点。
  4. 然后,我们将队首节点 self.head 和队尾节点 self.rear 都设置为 None,表示队列已经为空。
  5. 同时,我们减少队列的长度计数 _length
  6. 如果队列中有多个节点,我们需要遍历整个队列找到倒数第二个节点,即 cur 变量指向倒数第二个节点,prev 变量指向它的前一个节点。
  7. 我们遍历链表,将 prev 移动到 cur 的前一个节点,将 cur 移动到队列尾部节点,直到 cur 指向队尾节点。
  8. 在这个过程中,我们断开 prevcur 之间的链接,从而将队尾节点从链表中分离。
  9. 最后,我们将队尾节点 self.rear 更新为变量 prev,将队列的长度计数 _length 减少,并返回保存在变量 res 中的节点的数据,即完成了从队尾弹出元素的操作。
    def pop_right(self):
        if self.is_empty():
            raise ValueError('双端队列为空')
        elif self.head == self.rear:
            res = self.head
            self.head, self.rear = None, None
            self._length -= 1
            return res.data
        else:
            cur = self.head
            prev = None
            while cur.next:
                prev = cur
                cur = cur.next
            prev.next = None
            self.rear = prev
            self._length -= 1
            return cur.data

11.从队尾弹出元素的另一种实现

我们还可以通过另一种方式实现从队尾弹出元素的操作,相较于上一种方法代码更为简洁。

代码实现思路:

  1. 首先,我们检查双端队列是否为空。如果为空,抛出一个值错误,表示双端队列为空,不能执行弹出操作。
  2. 接着,我们检查队列是否只有一个节点。如果是,说明队列中只有一个节点,我们将队首节点 self.head 和队尾节点 self.rear 都设置为 None。
  3. 如果队列不为空且不止一个节点,我们先保存当前的队尾节点 self.rear 到变量 res 中,这将是我们要弹出的节点。
  4. 然后,我们遍历整个队列,将变量 cur 移动到倒数第二个节点的位置,将变量 prev 指向 cur 的前一个节点。
  5. 当遍历完成后,cur 将指向队尾节点的前一个节点,而 prev 指向 cur 的前一个节点。
  6. 我们将队尾节点 self.rear 更新为变量 cur,断开 curres 之间的链接,从而将队尾节点从链表中分离。
  7. 同时,我们减少队列的长度计数 _length
  8. 最后,我们返回保存在变量 res 中的节点的数据,即完成了从队尾弹出元素的操作。

代码如下:

    def pop_right2(self):
        if self.is_empty():
            raise ValueError('双端队列为空')
        else:
            if self.head == self.rear:
                res = self.head
                self.head, self.rear = None, None
            else:
                cur = self.head
                while cur.next != self.rear:
                    cur = cur.next
                res = self.rear
                self.rear = cur
                cur.next = None
            self._length -= 1
            return res.data

12.查看队首元素

要查看队列中的队首元素,我们可以编写一个简单的方法。

    def peek(self):
        if self.is_empty():
            raise ValueError('双端队列为空')
        else:
            return self.head.data

双端队列的完整代码

链表实现

'''
@Time    : 2023-08-15 15:01
@Author  : TAGRENLA
@File    : 双端队列  链表实现.py
'''
class Node():
    def __init__(self,data,next = None):
        self.data = data
        self.next = next


class Deque:
    def __init__(self):
        self.head = None
        self.rear = None
        self._length = 0

    def is_empty(self):
        return self._length == 0

    def length(self):
        return self._length

    def items(self):
        res = []
        cur = self.head
        while cur:
            res.append(cur.data)
            cur = cur.next
        return res


    def push(self,data):
        node = Node(data)
        if self.is_empty():
            self.head = node
            self.rear = node
        else:
            self.rear.next = node
            self.rear = node
        self._length += 1

    def push_left(self,data):
        node = Node(data)
        if self.is_empty():
            self.head = node
            self.rear = node
        else:
            node.next = self.head
            self.head = node
        self._length += 1

    def pop(self):
        # 弹出队首元素
        if self.is_empty():
            raise ValueError('双端队列为空')
        else:
            res = self.head
            self.head = self.head.next
            self._length -= 1
            # 处理只有尾节点的情况
            if self.head is None:
                self.rear = None
            return res.data

    def pop_right(self):
        # 弹出队尾元素
        if self.is_empty():
            raise ValueError('双端队列为空')
        elif self.head == self.rear:
            res = self.head
            self.head,self.rear = None,None
            self._length -= 1
            return res.data
        else:
            cur = self.head
            prev = None
            while cur.next:
                prev = cur
                cur = cur.next
            prev.next = None
            self.rear = prev
            self._length -= 1
            return cur.data

    def pop_right2(self):
        # 弹出队尾元素
        if self.is_empty():
            raise ValueError('双端队列为空')
        else:
            if self.head == self.rear:
                # 只有一个节点
                res = self.head
                self.head = None
                self.rear = None
            else:
                # 多个节点
                cur = self.head
                while cur.next != self.rear:
                    cur = cur.next
                res = self.rear
                self.rear = cur
                cur.next = None

            self._length -= 1
            return res.data

    def peek(self):
        # 返回队首元素
        if self.is_empty():
            raise ValueError('双端队列为空')
        else:
            return self.head.data

if __name__ == '__main__':
    deque = Deque()
    deque.push(1)
    deque.push(2)
    deque.push(3)
    deque.push(4)
    deque.push(5)
    deque.push(6)
    print(deque.pop())
    print(deque.pop_right())
    print(deque.pop())
    print(deque.pop_right())
    print(deque.items())
    # print(deque.items)
    # print(deque.peek())

双端队列顺序表实现

'''
@Time    : 2023-08-15 14:24
@Author  : TAGRENLA
@File    : 双端队列_顺序表.py
'''
# 顺序表实现
class Deque():
    def __init__(self):              # 初始化队列  顺序表实现
        self.items = []

    def is_empty(self):               # 返回队列是否为空
        return self.items == 0

    def length(self):               # 返回队列长度
        return len(self.items)

    def push(self,item):           # 在队尾添加元素
        self.items.append(item)

    def push_left(self,item):       # 在队首添加元素
        self.items.insert(0,item)

    def pop(self):                # 弹出队首元素
        return self.items.pop(0)

    def pop_right(self):         #弹出队尾元素
        return self.items.pop()

    def peek(self):            # 输出队首元素
        return self.items[0]

if __name__ == '__main__':
    deque = Deque()
    deque.push(1)
    deque.push(2)
    deque.push_left(3)
    deque.push_left(4)
    deque.push_left(5)
    deque.push_left(6)
    print(deque.items)
    print(deque.peek())

双端循环队列

什么是双端循环队列?

双端循环队列(Circular Deque),也被称为循环双端队列,是一种特殊类型的双端队列,具有循环性质。它的特点是队列的首尾是相连的,形成一个环,使得在队列满时可以重新利用之前的空间。双端循环队列常常用于需要高效利用存储空间的情况,比如循环缓冲区、操作系统任务调度等。

双端循环队列的特点包括:

  1. 循环性质: 在队列的逻辑结构中,队列的两端是相连的,使得队列成为一个环形的结构。这使得队列可以循环利用内存空间。
  2. 支持双向操作: 可以在队列的头部和尾部同时进行插入和删除操作,使其在处理不同问题时具有更灵活的用途。
  3. 循环索引: 为了实现循环性质,队列的实现通常使用一个循环索引,使队列在尾部元素之后绕回到头部元素。
  4. 大小限制: 和普通队列一样,循环双端队列可以有一个固定的容量上限。

双端循环队列在许多问题中具有实用性,例如在实现缓存、循环任务调度、模拟系统等方面都可以得到应用。通过在两端同时支持插入和删除操作,并且可以循环利用内存,它提供了更高效的数据管理方式。

collections模块实现

python中的collections模块中deque类就含有其功能,具体功能见下表:

方法名用途
append(x)在队尾添加元素 x
appendleft(x)在队首添加元素 x
pop()弹出并返回队尾的元素
popleft()弹出并返回队首的元素
extend(iterable)在队尾扩展添加一个可迭代对象的元素
extendleft(iterable)在队首扩展添加一个可迭代对象的元素
rotate(n=1)旋转队列的元素,正数向右旋转,负数向左旋转
count(x)计算队列中元素 x 的数量
remove(value)从队列中删除首次出现的值 value
reverse()反转队列中的元素
clear()清空队列中的所有元素
maxlen双端队列的最大长度

示例代码:

from collections import deque

# 创建一个双端队列
d = deque()

# 在队首和队尾插入元素
d.appendleft(1)  # 插入到队首
d.append(2)      # 插入到队尾
d.append(3)
d.appendleft(0)
d.append(4)

# 输出队列的元素以及队首和队尾元素
elements = list(d)  # 将deque转换为列表
print("队列元素:", elements)
print("队首元素:", d[0])  # 双端队列支持索引访问
print("队尾元素:", d[-1])

# 删除队首和队尾元素
d.popleft()  # 删除队首元素
d.pop()      # 删除队尾元素

# 输出删除后的队列元素以及队首和队尾元素
elements_after_deletion = list(d)
print("删除后的队列元素:", elements_after_deletion)
print("删除后的队首元素:", d[0])
print("删除后的队尾元素:", d[-1])

顺序表实现双端循环队列

class CircularDeque:
    def __init__(self, capacity):
        self.capacity = capacity
        self.queue = [None] * capacity
        self.front = 0
        self.rear = 0
        self.size = 0

    def is_empty(self):
        return self.size == 0

    def is_full(self):
        return self.size == self.capacity

    def insert_front(self, value):
        if self.is_full():
            return False
        self.front = (self.front - 1 + self.capacity) % self.capacity
        self.queue[self.front] = value
        self.size += 1
        return True

    def insert_last(self, value):
        if self.is_full():
            return False
        self.queue[self.rear] = value
        self.rear = (self.rear + 1) % self.capacity
        self.size += 1
        return True

    def delete_front(self):
        if self.is_empty():
            return False
        self.front = (self.front + 1) % self.capacity
        self.size -= 1
        return True

    def delete_last(self):
        if self.is_empty():
            return False
        self.rear = (self.rear - 1 + self.capacity) % self.capacity
        self.size -= 1
        return True

    def get_front(self):
        if self.is_empty():
            return None
        return self.queue[self.front]

    def get_rear(self):
        if self.is_empty():
            return None
        return self.queue[(self.rear - 1 + self.capacity) % self.capacity]


# 创建一个容量为5的双端循环队列
deque = CircularDeque(5)

# 在队首和队尾插入元素
deque.insert_front(1)
deque.insert_last(2)
deque.insert_last(3)
deque.insert_front(0)
deque.insert_last(4)

# 打印队列的元素和队首、队尾
print("队列元素:", [deque.queue[i] for i in range(deque.capacity)])
print("队首元素:", deque.get_front())
print("队尾元素:", deque.get_rear())

# 删除队首和队尾元素
deque.delete_front()
deque.delete_last()

# 打印删除后的队列元素和队首、队尾
print("删除后的队列元素:", [deque.queue[i] for i in range(deque.capacity)])
print("删除后的队首元素:", deque.get_front())
print("删除后的队尾元素:", deque.get_rear())

链表实现双端循环队列

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

class CircularDeque:
    def __init__(self, capacity):
        self.capacity = capacity
        self.size = 0
        self.front = None
        self.rear = None

    def is_empty(self):
        return self.size == 0

    def is_full(self):
        return self.size == self.capacity

    def insert_front(self, value):
        if self.is_full():
            return False
        new_node = Node(value)
        if self.is_empty():
            self.front = new_node
            self.rear = new_node
        else:
            new_node.next = self.front
            self.front = new_node
            self.rear.next = self.front  # 完成循环连接
        self.size += 1
        return True

    def insert_rear(self, value):
        if self.is_full():
            return False
        new_node = Node(value)
        if self.is_empty():
            self.front = new_node
            self.rear = new_node
        else:
            self.rear.next = new_node
            new_node.next = self.front  # 完成循环连接
            self.rear = new_node
        self.size += 1
        return True

    def delete_front(self):
        if self.is_empty():
            return False
        if self.front == self.rear:
            self.front = None
            self.rear = None
        else:
            self.front = self.front.next
            self.rear.next = self.front  # 更新循环连接
        self.size -= 1
        return True

    def delete_rear(self):
        if self.is_empty():
            return False
        if self.front == self.rear:
            self.front = None
            self.rear = None
        else:
            cur = self.front
            while cur.next != self.rear:
                cur = cur.next
            cur.next = self.front  # 更新循环连接
            self.rear = cur
        self.size -= 1
        return True

    def get_front(self):
        if self.is_empty():
            return None
        return self.front.value

    def get_rear(self):
        if self.is_empty():
            return None
        return self.rear.value

# 创建容量为5的循环双端队列
deque = CircularDeque(5)

# 在队首和队尾插入元素
deque.insert_front(1)
deque.insert_rear(2)
deque.insert_rear(3)
deque.insert_front(0)
deque.insert_rear(4)

# 输出队列的元素以及队首和队尾元素
elements = []
current = deque.front
for _ in range(deque.size):
    elements.append(current.value)
    current = current.next

print("队列元素:", elements)
print("队首元素:", deque.get_front())
print("队尾元素:", deque.get_rear())

# 删除队首和队尾元素
deque.delete_front()
deque.delete_rear()

# 输出删除后的队列元素以及队首和队尾元素
elements_after_deletion = []
current = deque.front
for _ in range(deque.size):
    elements_after_deletion.append(current.value)
    current = current.next

print("删除后的队列元素:", elements_after_deletion)
print("删除后的队首元素:", deque.get_front())
print("删除后的队尾元素:", deque.get_rear())
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TAGRENLA

您的打赏是我创作的动力,谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值