双端队列(Deque)原理及实现
原创
基本原理
如果你理解了前面讲解的内容,这个双端队列其实没啥可讲的了。所谓双端队列,主要是对比标准队列(FIFO 先进先出队列)多了一些操作罢了:
java
class MyDeque<E> {
    // 从队头插入元素,时间复杂度 O(1)
    void addFirst(E e);
    // 从队尾插入元素,时间复杂度 O(1)
    void addLast(E e);
    // 从队头删除元素,时间复杂度 O(1)
    E removeFirst();
    // 从队尾删除元素,时间复杂度 O(1)
    E removeLast();
    // 查看队头元素,时间复杂度 O(1)
    E peekFirst();
    // 查看队尾元素,时间复杂度 O(1)
    E peekLast();
}cpp
template<typename E>
class MyDeque {
public:
    // 从队头插入元素,时间复杂度 O(1)
    void addFirst(E e);
    // 从队尾插入元素,时间复杂度 O(1)
    void addLast(E e);
    // 从队头删除元素,时间复杂度 O(1)
    E removeFirst();
    // 从队尾删除元素,时间复杂度 O(1)
    E removeLast();
    // 查看队头元素,时间复杂度 O(1)
    E peekFirst();
    // 查看队尾元素,时间复杂度 O(1)
    E peekLast();
};python
class MyDeque:
    # 从队头插入元素,时间复杂度 O(1)
    def add_first(self, e):
        pass
    # 从队尾插入元素,时间复杂度 O(1)
    def add_last(self, e):
        pass
    # 从队头删除元素,时间复杂度 O(1)
    def remove_first(self):
        pass
    # 从队尾删除元素,时间复杂度 O(1)
    def remove_last(self):
        pass
    # 查看队头元素,时间复杂度 O(1)
    def peek_first(self):
        pass
    # 查看队尾元素,时间复杂度 O(1)
    def peek_last(self):
        passgo
// MyDeque represents a deque (double-ended queue) data structure
type MyDeque[E any] struct {
}
// 从队头插入元素,时间复杂度 O(1)
func (d *MyDeque[E]) AddFirst(e E) {
}
// 从队尾插入元素,时间复杂度 O(1)
func (d *MyDeque[E]) AddLast(e E) {
}
// 从队头删除元素,时间复杂度 O(1)
func (d *MyDeque[E]) RemoveFirst() E {
}
// 从队尾删除元素,时间复杂度 O(1)
func (d *MyDeque[E]) RemoveLast() E {
}
// 查看队头元素,时间复杂度 O(1)
func (d *MyDeque[E]) PeekFirst() E {
}
// 查看队尾元素,时间复杂度 O(1)
func (d *MyDeque[E]) PeekLast() E {
}javascript
var MyDeque = function() {
    // 从队头插入元素,时间复杂度 O(1)
    this.addFirst = function(e) {};
    // 从队尾插入元素,时间复杂度 O(1)
    this.addLast = function(e) {};
    // 从队头删除元素,时间复杂度 O(1)
    this.removeFirst = function() {};
    // 从队尾删除元素,时间复杂度 O(1)
    this.removeLast = function() {};
    // 查看队头元素,时间复杂度 O(1)
    this.peekFirst = function() {};
    // 查看队尾元素,时间复杂度 O(1)
    this.peekLast = function() {};
};标准队列 只能在队尾插入元素,队头删除元素,而双端队列的队头和队尾都可以插入或删除元素。
普通队列就好比排队买票,先来的先买,后来的后买;而双端队列就好比一个过街天桥,两端都可以随意进出。当然,双端队列的元素就不再满足「先进先出」了,因为它比较灵活嘛。
在做算法题的场景中,双端队列用的不算很多。感觉只有 Python 用到的多一些,因为 Python 标准库没有提供内置的栈和队列,一般会用双端队列来模拟标准队列。
用链表实现双端队列
很简单吧,直接复用我们之前实现的 MyLinkedList 类,或者使用编程语言标准库提供的双链表结构就行了。因为双链表本就支持  时间复杂度在链表的头尾增删元素:
java
import java.util.LinkedList;
public class MyListDeque<E> {
    private LinkedList<E> list = new LinkedList<>();
    // 从队头插入元素,时间复杂度 O(1)
    void addFirst(E e) {
        list.addFirst(e);
    }
    // 从队尾插入元素,时间复杂度 O(1)
    void addLast(E e) {
        list.addLast(e);
    }
    // 从队头删除元素,时间复杂度 O(1)
    E removeFirst() {
        return list.removeFirst();
    }
    // 从队尾删除元素,时间复杂度 O(1)
    E removeLast() {
        return list.removeLast();
    }
    // 查看队头元素,时间复杂度 O(1)
    E peekFirst() {
        return list.getFirst();
    }
    // 查看队尾元素,时间复杂度 O(1)
    E peekLast() {
        return list.getLast();
    }
    public static void main(String[] args) {
        MyListDeque<Integer> deque = new MyListDeque<>();
        deque.addFirst(1);
        deque.addFirst(2);
        deque.addLast(3);
        deque.addLast(4);
        System.out.println(deque.removeFirst()); // 2
        System.out.println(deque.removeLast()); // 4
        System.out.println(deque.peekFirst()); // 1
        System.out.println(deque.peekLast()); // 3
    }
}cpp
#include <iostream>
#include <list>
template<typename E>
class MyListDeque {
    std::list<E> list;
public:
    // 从队头插入元素,时间复杂度 O(1)
    void addFirst(const E &e) {
        list.push_front(e);
    }
    // 从队尾插入元素,时间复杂度 O(1)
    void addLast(const E &e) {
        list.push_back(e);
    }
    // 从队头删除元素,时间复杂度 O(1)
    E removeFirst() {
        E firstElement = list.front();
        list.pop_front();
        return firstElement;
    }
    // 从队尾删除元素,时间复杂度 O(1)
    E removeLast() {
        E lastElement = list.back();
        list.pop_back();
        return lastElement;
    }
    // 查看队头元素,时间复杂度 O(1)
    E peekFirst() {
        return list.front();
    }
    // 查看队尾元素,时间复杂度 O(1)
    E peekLast() {
        return list.back();
    }
};
int main() {
    MyListDeque<int> deque;
    deque.addFirst(1);
    deque.addFirst(2);
    deque.addLast(3);
    deque.addLast(4);
    std::cout << deque.removeFirst() << std::endl; // 2
    std::cout << deque.removeLast() << std::endl; // 4
    std::cout << deque.peekFirst() << std::endl; // 1
    std::cout << deque.peekLast() << std::endl; // 3
    return 0;
}python
class MyListDeque:
    def __init__(self):
        # 使用我们之前实现的 `MyLinkedList` 类
        self.list = MyLinkedList()
    # 从队头插入元素,时间复杂度 O(1)
    def add_first(self, e):
        self.list.add_first(e)
    # 从队尾插入元素,时间复杂度 O(1)
    def add_last(self, e):
        self.list.add_last(e)
    # 从队头删除元素,时间复杂度 O(1)
    def remove_first(self):
        return self.list.remove_first()
    # 从队尾删除元素,时间复杂度 O(1)
    def remove_last(self):
        return self.list.remove_last()
    # 查看队头元素,时间复杂度 O(1)
    def peek_first(self):
        return self.list.get_first()
    # 查看队尾元素,时间复杂度 O(1)
    def peek_last(self):
        return self.list.get_last()
# 使用示例
my_deque = MyListDeque()
my_deque.add_first(1)
my_deque.add_first(2)
my_deque.add_last(3)
my_deque.add_last(4)
print(my_deque.remove_first())  # 2
print(my_deque.remove_last())  # 4
print(my_deque.peek_first())  # 1
print(my_deque.peek_last())  # 3go
package main
import (
	"container/list"
	"fmt"
)
type MyListDeque struct {
	list *list.List
}
func NewMyListDeque() *MyListDeque {
	return &MyListDeque{list: list.New()}
}
// 从队头插入元素,时间复杂度 O(1)
func (d *MyListDeque) AddFirst(e interface{}) {
	d.list.PushFront(e)
}
// 从队尾插入元素,时间复杂度 O(1)
func (d *MyListDeque) AddLast(e interface{}) {
	d.list.PushBack(e)
}
// 从队头删除元素,时间复杂度 O(1)
func (d *MyListDeque) RemoveFirst() interface{} {
	if elem := d.list.Front(); elem != nil {
		return d.list.Remove(elem)
	}
	return nil
}
// 从队尾删除元素,时间复杂度 O(1)
func (d *MyListDeque) RemoveLast() interface{} {
	if elem := d.list.Back(); elem != nil {
		return d.list.Remove(elem)
	}
	return nil
}
// 查看队头元素,时间复杂度 O(1)
func (d *MyListDeque) PeekFirst() interface{} {
	if elem := d.list.Front(); elem != nil {
		return elem.Value
	}
	return nil
}
// 查看队尾元素,时间复杂度 O(1)
func (d *MyListDeque) PeekLast() interface{} {
	if elem := d.list.Back(); elem != nil {
		return elem.Value
	}
	return nil
}
func main() {
	deque := NewMyListDeque()
	deque.AddFirst(1)
	deque.AddFirst(2)
	deque.AddLast(3)
	deque.AddLast(4)
	fmt.Println(deque.RemoveFirst()) // 2
	fmt.Println(deque.RemoveLast())  // 4
	fmt.Println(deque.PeekFirst())   // 1
	fmt.Println(deque.PeekLast())    // 3
}javascript
class MyListDeque {
    constructor() {
        // JavaScript 中没有提供双链表,所以使用之前实现的 `MyLinkedList` 类
        this.list = new MyLinkedList();
    }
    // 从队头插入元素,时间复杂度 O(1)
    addFirst(e) {
        this.list.addFirst(e);
    }
    // 从队尾插入元素,时间复杂度 O(1)
    addLast(e) {
        this.list.addLast(e);
    }
    // 从队头删除元素,时间复杂度 O(1)
    removeFirst() {
        return this.list.removeFirst();
    }
    // 从队尾删除元素,时间复杂度 O(1)
    removeLast() {
        return this.list.removeLast();
    }
    // 查看队头元素,时间复杂度 O(1)
    peekFirst() {
        return this.list.getFirst();
    }
    peekLast() {
        return this.list.getLast();
    }
}
// 使用示例
const myDeque = new MyListDeque();
myDeque.addFirst(1);
myDeque.addFirst(2);
myDeque.addLast(3);
myDeque.addLast(4);
console.log(myDeque.removeFirst()); // 2
console.log(myDeque.removeLast()); // 4
console.log(myDeque.peekFirst()); // 1
console.log(myDeque.peekLast()); // 3用数组实现双端队列
也很简单吧,直接复用我们在 环形数组技巧 中实现的 CycleArray 提供的方法就行了。环形数组头尾增删元素的复杂度都是 :
java
class MyArrayDeque<E> {
    private CycleArray<E> arr = new CycleArray<>();
    // 从队头插入元素,时间复杂度 O(1)
    void addFirst(E e) {
        arr.addFirst(e);
    }
    // 从队尾插入元素,时间复杂度 O(1)
    void addLast(E e) {
        arr.addLast(e);
    }
    // 从队头删除元素,时间复杂度 O(1)
    E removeFirst() {
        return arr.removeFirst();
    }
    // 从队尾删除元素,时间复杂度 O(1)
    E removeLast() {
        return arr.removeLast();
    }
    // 查看队头元素,时间复杂度 O(1)
    E peekFirst() {
        return arr.getFirst();
    }
    // 查看队尾元素,时间复杂度 O(1)
    E peekLast() {
        return arr.getLast();
    }
}cpp
template <typename E>
class CycleArray {
public:
    void addFirst(E e);
    void addLast(E e);
    E removeFirst();
    E removeLast();
    E getFirst();
    E getLast();
};
template <typename E>
class MyArrayDeque {
private:
    CycleArray<E> arr;
public:
    // 从队头插入元素,时间复杂度 O(1)
    void addFirst(E e) {
        arr.addFirst(e);
    }
    // 从队尾插入元素,时间复杂度 O(1)
    void addLast(E e) {
        arr.addLast(e);
    }
    // 从队头删除元素,时间复杂度 O(1)
    E removeFirst() {
        return arr.removeFirst();
    }
    // 从队尾删除元素,时间复杂度 O(1)
    E removeLast() {
        return arr.removeLast();
    }
    // 查看队头元素,时间复杂度 O(1)
    E peekFirst() {
        return arr.getFirst();
    }
    // 查看队尾元素,时间复杂度 O(1)
    E peekLast() {
        return arr.getLast();
    }
};python
class MyArrayDeque:
    def __init__(self):
        self.arr = CycleArray()
    # 从队头插入元素,时间复杂度 O(1)
    def add_first(self, e):
        self.arr.add_first(e)
    # 从队尾插入元素,时间复杂度 O(1)
    def add_last(self, e):
        self.arr.add_last(e)
    # 从队头删除元素,时间复杂度 O(1)
    def remove_first(self):
        return self.arr.remove_first()
    # 从队尾删除元素,时间复杂度 O(1)
    def remove_last(self):
        return self.arr.remove_last()
    # 查看队头元素,时间复杂度 O(1)
    def peek_first(self):
        return self.arr.get_first()
    # 查看队尾元素,时间复杂度 O(1)
    def peek_last(self):
        return self.arr.get_last()go
// MyArrayDeque is a generic deque implemented using a CycleArray
type MyArrayDeque[E any] struct {
	arr CycleArray[E]
}
// NewMyArrayDeque creates a new MyArrayDeque
func NewMyArrayDeque[E any]() *MyArrayDeque[E] {
	return &MyArrayDeque[E]{arr: CycleArray[E]{}}
}
// 从队头插入元素,时间复杂度 O(1)
func (d *MyArrayDeque[E]) AddFirst(e E) {
	d.arr.AddFirst(e)
}
// 从队尾插入元素,时间复杂度 O(1)
func (d *MyArrayDeque[E]) AddLast(e E) {
	d.arr.AddLast(e)
}
// 从队头删除元素,时间复杂度 O(1)
func (d *MyArrayDeque[E]) RemoveFirst() E {
	return d.arr.RemoveFirst()
}
// 从队尾删除元素,时间复杂度 O(1)
func (d *MyArrayDeque[E]) RemoveLast() E {
	return d.arr.RemoveLast()
}
// 查看队头元素,时间复杂度 O(1)
func (d *MyArrayDeque[E]) PeekFirst() E {
	return d.arr.GetFirst()
}
// 查看队尾元素,时间复杂度 O(1)
func (d *MyArrayDeque[E]) PeekLast() E {
	return d.arr.GetLast()
}javascript
var MyArrayDeque = function() {
    this.arr = new CycleArray();
    // 从队头插入元素,时间复杂度 O(1)
    this.addFirst = function(e) {
        this.arr.addFirst(e);
    };
    // 从队尾插入元素,时间复杂度 O(1)
    this.addLast = function(e) {
        this.arr.addLast(e);
    };
    // 从队头删除元素,时间复杂度 O(1)
    this.removeFirst = function() {
        return this.arr.removeFirst();
    };
    // 从队尾删除元素,时间复杂度 O(1)
    this.removeLast = function() {
        return this.arr.removeLast();
    };
    // 查看队头元素,时间复杂度 O(1)
    this.peekFirst = function() {
        return this.arr.getFirst();
    };
    // 查看队尾元素,时间复杂度 O(1)
    this.peekLast = function() {
        return this.arr.getLast();
    };
};