10.双端队列

本文详细介绍了使用Java内置双向链表和数组实现循环双端队列(CircularDeque)的方法,对比了它们在底层数据结构、时间复杂度、空间复杂度和实现细节上的特点,以及适用的不同场景。
摘要由CSDN通过智能技术生成

10.双端队列

链表实现

使用Java内置双向链表实现。
代码和数组实现放在后边。

数组实现

从头部插入:

a:L==0,将加入的值放在末尾k-1位置,L= k-1;
b:L≠≠0,将加入的值放在L-1的位置,L = L–。

从头部弹出:

a:如果L==k-1(L当前在k-1位置),弹出当前L位置的值,L = 0;
b;如果L≠≠k-1,弹出当前L位置的值,L++,(往后走)。

从尾部插入:

a:当R==k-1,将新加入的元素放在0位置,R=0;
b:当R≠≠k-1,将新加入的元素放在R+1位置,R++。

从尾部弹出:

a:R==0,将当前R位置的元素弹出,R=k-1
b:R≠≠0,将当前位置的元素弹出,R–。

代码

// 定义一个名为 CircularDeque 的公共类
public class CircularDeque {

    // 定义一个名为 MyCircularDeque1 的内部类,用于实现基于链表的循环双端队列
    class MyCircularDeque1 {
        // 使用 LinkedList 实现的双端队列
        public Deque<Integer> deque = new LinkedList<>();
        // 队列的当前大小
        public int size;
        // 队列的最大容量
        public int limit;

        // 构造方法,初始化队列的大小和容量
        public MyCircularDeque1(int k) {
            size = 0; // 初始化队列为空
            limit = k; // 设置队列的最大容量
        }

        // 在队列前端插入一个元素
        public boolean insertFront(int value) {
            // 如果队列已满,返回 false
            if (isFull()) {
                return false;
            } else {
                // 否则,将元素插入队列前端,并增加队列大小
                deque.offerFirst(value);
                size++;
                // 插入成功,返回 true
                return true;
            }
        }

        // 在队列后端插入一个元素
        public boolean insertLast(int value) {
            // 如果队列已满,返回 false
            if (isFull()) {
                return false;
            } else {
                // 否则,将元素插入队列后端,并增加队列大小
                deque.offerLast(value);
                size++;
                // 插入成功,返回 true
                return true;
            }
        }

        // 删除队列前端的元素
        public boolean deleteFront() {
            // 如果队列为空,返回 false
            if (isEmpty()) {
                return false;
            } else {
                // 否则,删除队列前端的元素,并减少队列大小
                size--;
                deque.pollFirst(); // 移除并返回队列的第一个元素
                // 删除成功,返回 true
                return true;
            }
        }

        // 删除队列后端的元素
        public boolean deleteLast() {
            // 如果队列为空,返回 false
            if (isEmpty()) {
                return false;
            } else {
                // 否则,删除队列后端的元素,并减少队列大小
                size--;
                deque.pollLast(); // 移除并返回队列的最后一个元素
                // 删除成功,返回 true
                return true;
            }
        }

        // 获取队列前端的元素,不删除
        public int getFront() {
            // 如果队列为空,返回 -1
            if (isEmpty()) {
                return -1;
            } else {
                // 否则,返回队列前端的元素
                return deque.peekFirst();
            }
        }

        // 获取队列后端的元素,不删除
        public int getRear() {
            // 如果队列为空,返回 -1
            if (isEmpty()) {
                return -1;
            } else {
                // 否则,返回队列后端的元素
                return deque.peekLast();
            }
        }

        // 检查队列是否为空
        public boolean isEmpty() {
            return size == 0; // 如果队列大小为 0,则队列为空
        }

        // 检查队列是否已满
        public boolean isFull() {
            return size == limit; // 如果队列大小等于最大容量,则队列已满
        }
    }

    // 定义一个名为 MyCircularDeque2 的内部类,用于实现基于数组的循环双端队列
    class MyCircularDeque2 {
        // 使用数组实现的循环双端队列
        public int[] deque;
        // 队列的左指针
        public int l;
        // 队列的右指针
        public int r;
        // 队列的当前大小
        public int size;
        // 队列的最大容量
        public int limit;

        // 构造方法,初始化队列的大小、容量和指针
        public MyCircularDeque2(int k) {
            deque = new int[k]; // 创建一个大小为 k 的数组用于存储队列元素
            l = r = size = 0; // 初始化左指针、右指针和队列大小为 0
            limit = k; // 设置队列的最大容量
        }

        // 在队列前端插入一个元素
        public boolean insertFront(int value) {
            // 如果队列已满,返回 false
            if (isFull()) {
                return false;
            } else {
                // 如果队列为空,初始化左右指针并插入元素
                if (isEmpty()) {
                    l = r = 0;
                    deque[0] = value;
                } else {
                    // 否则,更新左指针并插入元素
                    l = l == 0 ? (limit - 1) : (l - 1);
                    deque[l] = value;
                }
                // 插入成功,增加队列大小并返回 true
                size++;
                return true;
            }
        }

        // 在队列后端插入一个元素
        public boolean insertLast(int value) {
            // 如果队列已满,返回 false
            if (isFull()) {
                return false;
            } else {
                // 如果队列为空,初始化左右指针并插入元素
                if (isEmpty()) {
                    l = r = 0;
                    deque[0] = value;
                } else {
                    // 否则,更新右指针并插入元素
                    r = r == limit - 1 ? 0 : (r + 1);
                    deque[r] = value;
                }
                // 插入成功,增加队列大小并返回 true
                size++;
                return true;
            }
        }

        // 删除队列前端的元素并更新左指针
        public boolean deleteFront() {
            // 如果队列为空,返回 false
            if (isEmpty()) {
                return false;
            } else {
                // 否则,更新左指针
                l = (l == limit - 1) ? 0 : (l + 1);
                // 删除成功,减少队列大小并返回 true
                size--;
                // 注意:这里没有实际删除数组中的元素,因为数组的元素会在插入时覆盖
                return true;
            }
        }

        // 删除队列后端的元素并更新右指针
        public boolean deleteLast() {
            // 如果队列为空,返回 false
            if (isEmpty()) {
                return false;
            } else {
                // 否则,更新右指针
                r = r == 0 ? (limit - 1) : (r - 1);
                // 删除成功,减少队列大小并返回 true
                size--;
                // 注意:这里没有实际删除数组中的元素,因为数组的元素会在插入时覆盖
                return true;
            }
        }

        // 获取队列前端的元素,不删除
        public int getFront() {
            // 如果队列为空,返回 -1
            if (isEmpty()) {
                return -1;
            } else {
                // 否则,返回队列前端的元素
                return deque[l];
            }
        }

        // 获取队列后端的元素,不删除
        public int getRear() {
            // 如果队列为空,返回 -1
            if (isEmpty()) {
                return -1;
            } else {
                // 否则,返回队列后端的元素
                return deque[r];
            }
        }

        // 检查队列是否为空
        public boolean isEmpty() {
            return size == 0; // 如果队列大小为 0,则队列为空
        }

        // 检查队列是否已满
        public boolean isFull() {
            return size == limit; // 如果队列大小等于最大容量,则队列已满
        }
    }
}

二者区别:

底层数据结构:

MyCircularDeque1 使用 LinkedList 作为底层数据结构。LinkedList 是一个链表,它由节点组成,每个节点包含数据和指向下一个节点的引用。使用链表可以轻松地在任何位置添加或删除元素,因为这些操作不需要移动其他元素。 
MyCircularDeque2 使用数组作为底层数据结构。数组是一个固定大小的连续内存块,可以存储相同类型的元素。在数组中插入和删除元素可能需要移动其他元素,但访问元素的速度快。

时间复杂度:

MyCircularDeque1 的插入和删除操作在链表中通常是 O(1) 时间复杂度,因为链表的节点可以在任何位置添加或删除,而不需要重新排列其他节点。 
MyCircularDeque2 的插入和删除操作在数组中也是 O(1) 时间复杂度,但这是因为环形队列的特性允许在数组的两端进行操作,而不需要移动其他元素。然而,如果数组需要扩容,这将是一个 O(n)的操作,因为所有元素都需要移动到新的数组。 

空间复杂度:

使用 LinkedList 的 MyCircularDeque1 可能需要更多的空间来存储节点和它们的引用,这可能导致更高的内存	开销。 
使用数组的 MyCircularDeque2 通常具有更低的内存开销,因为它只需要存储连续的元素,没有额外的引用开销。但是,数组可能需要预留额外的空间以备将来扩容。 

实现细节:

MyCircularDeque1 的实现较为简单,因为它依赖于 LinkedList 的内置方法来处理插入和删除操作。 	
MyCircularDeque2 的实现需要手动管理数组的索引,以及在插入和删除时正确更新这些索引。这需要额外的逻辑来处理环形队列的边界条件。

性能特点:

MyCircularDeque1 在插入和删除操作上具有很好的性能,但是在随机访问时可能比数组慢,因为链表需要遍历节点。 
MyCircularDeque2 在随机访问时性能较好,因为可以直接通过索引访问数组中的元素。但是,如果队列经常需要扩容,性能可能会受到影响。

总的来说,两种实现各有优势和适用场景。MyCircularDeque1 更适合于频繁插入和删除的场景,而 MyCircularDeque2
更适合于固定大小或较少扩容需求的场景,并且需要快速随机访问元素。在实际应用中,应根据具体需求和性能要求选择合适的实现。

力扣测试链接

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值