hello-algo/docs/chapter_stack_and_queue/deque.md
zhuoqinyue c4b510038c
Add linkedlist_deque for typescript and javascript (#326)
* add linkedlist_deque for typescript and javascript

* update typescript and javascript examples for the deque.md

* keep the same name

* update the doc

* Update deque.md

---------

Co-authored-by: steak-zhuo <zhuoqinyue@gmail.com>
Co-authored-by: Yudong Jin <krahets@163.com>
2023-02-06 01:31:09 +08:00

20 KiB
Raw Blame History

comments
true

5.3. 双向队列

对于队列,我们只能在头部删除或在尾部添加元素,而「双向队列 Deque」更加灵活在其头部和尾部都能执行元素添加或删除操作。

deque_operations

Fig. 双向队列的操作

5.3.1. 双向队列常用操作

双向队列的常用操作见下表,方法名需根据特定语言来确定。

Table. 双向队列的常用操作

方法名 描述 时间复杂度
pushFirst() 将元素添加至队首 O(1)
pushLast() 将元素添加至队尾 O(1)
pollFirst() 删除队首元素 O(1)
pollLast() 删除队尾元素 O(1)
peekFirst() 访问队首元素 O(1)
peekLast() 访问队尾元素 O(1)
size() 获取队列的长度 O(1)
isEmpty() 判断队列是否为空 O(1)

相同地,我们可以直接使用编程语言实现好的双向队列类。

=== "Java"

```java title="deque.java"
/* 初始化双向队列 */
Deque<Integer> deque = new LinkedList<>();

/* 元素入队 */
deque.offerLast(2);   // 添加至队尾
deque.offerLast(5);
deque.offerLast(4);
deque.offerFirst(3);  // 添加至队首
deque.offerFirst(1);

/* 访问元素 */
int peekFirst = deque.peekFirst();  // 队首元素
int peekLast = deque.peekLast();    // 队尾元素

/* 元素出队 */
int pollFirst = deque.pollFirst();  // 队首元素出队
int pollLast = deque.pollLast();    // 队尾元素出队

/* 获取双向队列的长度 */
int size = deque.size();

/* 判断双向队列是否为空 */
boolean isEmpty = deque.isEmpty();
```

=== "C++"

```cpp title="deque.cpp"
/* 初始化双向队列 */
deque<int> deque;

/* 元素入队 */
deque.push_back(2);   // 添加至队尾
deque.push_back(5);
deque.push_back(4);
deque.push_front(3);  // 添加至队首
deque.push_front(1);

/* 访问元素 */
int front = deque.front(); // 队首元素
int back = deque.back();   // 队尾元素

/* 元素出队 */
deque.pop_front();  // 队首元素出队
deque.pop_back();   // 队尾元素出队

/* 获取双向队列的长度 */
int size = deque.size();

/* 判断双向队列是否为空 */
bool empty = deque.empty();
```

=== "Python"

```python title="deque.py"
""" 初始化双向队列 """
duque = deque()

""" 元素入队 """
duque.append(2)      # 添加至队尾
duque.append(5)
duque.append(4)
duque.appendleft(3)  # 添加至队首
duque.appendleft(1)

""" 访问元素 """
front = duque[0]  # 队首元素
rear = duque[-1]  # 队尾元素

""" 元素出队 """
pop_front = duque.popleft()  # 队首元素出队
pop_rear = duque.pop()       # 队尾元素出队

""" 获取双向队列的长度 """
size = len(duque)

""" 判断双向队列是否为空 """
is_empty = len(duque) == 0
```

=== "Go"

```go title="deque_test.go"
/* 初始化双向队列 */
// 在 Go 中,将 list 作为双向队列使用
deque := list.New()

/* 元素入队 */
deque.PushBack(2)      // 添加至队尾
deque.PushBack(5)
deque.PushBack(4)
deque.PushFront(3)     // 添加至队首
deque.PushFront(1)

/* 访问元素 */
front := deque.Front() // 队首元素
rear := deque.Back()   // 队尾元素

/* 元素出队 */
deque.Remove(front)    // 队首元素出队
deque.Remove(rear)     // 队尾元素出队

/* 获取双向队列的长度 */
size := deque.Len()

/* 判断双向队列是否为空 */
isEmpty := deque.Len() == 0
```

=== "JavaScript"

```js title="deque.js"
/* 初始化双向队列 */
// JavaScript 没有内置的双端队列,只能把 Array 当作双端队列来使用
const deque = [];

/* 元素入队 */
deque.push(2);
deque.push(5);
deque.push(4);
// 请注意由于是数组unshift() 方法的时间复杂度为 O(n)
deque.unshift(3);
deque.unshift(1);
console.log("双向队列 deque = ", deque);

/* 访问元素 */
const peekFirst = deque[0];
console.log("队首元素 peekFirst = " + peekFirst);
const peekLast = deque[deque.length - 1];
console.log("队尾元素 peekLast = " + peekLast);

/* 元素出队 */
// 请注意由于是数组shift() 方法的时间复杂度为 O(n)
const popFront = deque.shift();
console.log("队首出队元素 popFront = " + popFront + ",队首出队后 deque = " + deque);
const popBack = deque.pop();
console.log("队尾出队元素 popBack = " + popBack + ",队尾出队后 deque = " + deque);

/* 获取双向队列的长度 */
const size = deque.length;
console.log("双向队列长度 size = " + size);

/* 判断双向队列是否为空 */
const isEmpty = size === 0;
console.log("双向队列是否为空 = " + isEmpty);
```

=== "TypeScript"

```typescript title="deque.ts"
/* 初始化双向队列 */
// TypeScript 没有内置的双端队列,只能把 Array 当作双端队列来使用
const deque: number[] = [];

/* 元素入队 */
deque.push(2);
deque.push(5);
deque.push(4);
// 请注意由于是数组unshift() 方法的时间复杂度为 O(n)
deque.unshift(3);
deque.unshift(1);
console.log("双向队列 deque = ", deque);

/* 访问元素 */
const peekFirst: number = deque[0];
console.log("队首元素 peekFirst = " + peekFirst);
const peekLast: number = deque[deque.length - 1];
console.log("队尾元素 peekLast = " + peekLast);

/* 元素出队 */
// 请注意由于是数组shift() 方法的时间复杂度为 O(n)
const popFront: number = deque.shift() as number;
console.log("队首出队元素 popFront = " + popFront + ",队首出队后 deque = " + deque);
const popBack: number = deque.pop() as number;
console.log("队尾出队元素 popBack = " + popBack + ",队尾出队后 deque = " + deque);

/* 获取双向队列的长度 */
const size: number = deque.length;
console.log("双向队列长度 size = " + size);

/* 判断双向队列是否为空 */
const isEmpty: boolean = size === 0;
console.log("双向队列是否为空 = " + isEmpty);
```

=== "C"

```c title="deque.c"

```

=== "C#"

```csharp title="deque.cs"
/* 初始化双向队列 */
// 在 C# 中,将链表 LinkedList 看作双向队列来使用
LinkedList<int> deque = new LinkedList<int>();

/* 元素入队 */
deque.AddLast(2);   // 添加至队尾
deque.AddLast(5);
deque.AddLast(4);
deque.AddFirst(3);  // 添加至队首
deque.AddFirst(1);

/* 访问元素 */
int peekFirst = deque.First.Value;  // 队首元素
int peekLast = deque.Last.Value;    // 队尾元素

/* 元素出队 */
deque.RemoveFirst();  // 队首元素出队
deque.RemoveLast();   // 队尾元素出队

/* 获取双向队列的长度 */
int size = deque.Count;

/* 判断双向队列是否为空 */
bool isEmpty = deque.Count == 0;
```

=== "Swift"

```swift title="deque.swift"
/* 初始化双向队列 */
// Swift 没有内置的双向队列类,可以把 Array 当作双向队列来使用
var deque: [Int] = []

/* 元素入队 */
deque.append(2) // 添加至队尾
deque.append(5)
deque.append(4)
deque.insert(3, at: 0) // 添加至队首
deque.insert(1, at: 0)

/* 访问元素 */
let peekFirst = deque.first! // 队首元素
let peekLast = deque.last! // 队尾元素

/* 元素出队 */
// 使用 Array 模拟时 pollFirst 的复杂度为 O(n)
let pollFirst = deque.removeFirst() // 队首元素出队
let pollLast = deque.removeLast() // 队尾元素出队

/* 获取双向队列的长度 */
let size = deque.count

/* 判断双向队列是否为空 */
let isEmpty = deque.isEmpty
```

=== "Zig"

```zig title="deque.zig"

```

5.3.2. 双向队列实现

双向队列需要一种可以在两端添加、两端删除的数据结构。与队列的实现方法类似,双向队列也可以使用双向链表和循环数组来实现。

基于双向链表的实现

我们将双向链表的头结点和尾结点分别看作双向队列的队首和队尾,并且实现在两端都能添加与删除结点。

=== "LinkedListDeque" linkedlist_deque

=== "pushLast()" linkedlist_deque_push_last

=== "pushFirst()" linkedlist_deque_push_first

=== "pollLast()" linkedlist_deque_poll_last

=== "pollFirst()" linkedlist_deque_poll_first

以下是使用双向链表实现双向队列的示例代码。

=== "Java"

```java title="linkedlist_deque.java"
/* 双向链表结点 */
class ListNode {
    int val;       // 结点值
    ListNode next; // 后继结点引用(指针)
    ListNode prev; // 前驱结点引用(指针)
    ListNode(int val) {
        this.val = val;
        prev = next = null;
    }
}

/* 基于双向链表实现的双向队列 */
class LinkedListDeque {
private ListNode front, rear; // 头结点 front ,尾结点 rear
private int size = 0;         // 双向队列的长度

    public LinkedListDeque() {
        front = rear = null;
    }

    /* 获取双向队列的长度 */
    public int size() {
        return size;
    }

    /* 判断双向队列是否为空 */
    public boolean isEmpty() {
        return size() == 0;
    }

    /* 入队操作 */
    private void push(int num, boolean isFront) {
        ListNode node = new ListNode(num);
        // 若链表为空,则令 front, rear 都指向 node
        if (isEmpty())
            front = rear = node;
        // 队首入队操作
        else if (isFront) {
            // 将 node 添加至链表头部
            front.prev = node;
            node.next = front;
            front = node; // 更新头结点
        // 队尾入队操作
        } else {
            // 将 node 添加至链表尾部
            rear.next = node;
            node.prev = rear;
            rear = node;  // 更新尾结点
        }
        size++; // 更新队列长度
    }

    /* 队首入队 */
    public void pushFirst(int num) {
        push(num, true);
    }

    /* 队尾入队 */
    public void pushLast(int num) {
        push(num, false);
    }

    /* 出队操作 */
    private Integer poll(boolean isFront) {
        // 若队列为空,直接返回 null
        if (isEmpty())
            return null;
        int val;
        // 队首出队操作
        if (isFront) {
            val = front.val; // 暂存头结点值
            // 删除头结点
            ListNode fNext = front.next;
            if (fNext != null) {
                fNext.prev = null;
                front.next = null;
            }
            front = fNext;   // 更新头结点
        // 队尾出队操作
        } else {
            val = rear.val;  // 暂存尾结点值
            // 删除尾结点
            ListNode rPrev = rear.prev;
            if (rPrev != null) {
                rPrev.next = null;
                rear.prev = null;
            }
            rear = rPrev;    // 更新尾结点
        }
        size--; // 更新队列长度
        return val;
    }

    /* 队首出队 */
    public Integer pollFirst() {
        return poll(true);
    }

    /* 队尾出队 */
    public Integer pollLast() {
        return poll(false);
    }

    /* 访问队首元素 */
    public Integer peekFirst() {
        return isEmpty() ? null : front.val;
    }

    /* 访问队尾元素 */
    public Integer peekLast() {
        return isEmpty() ? null : rear.val;
    }
}
```

=== "C++"

```cpp title="linkedlist_deque.cpp"

```

=== "Python"

```python title="linkedlist_deque.py"

```

=== "Go"

```go title="linkedlist_deque.go"

```

=== "JavaScript"

```js title="linkedlist_deque.js"
/* 双向链表结点 */
class ListNode {
    prev;   // 前驱结点引用 (指针)
    next;   // 后继结点引用 (指针)
    val;    // 结点值
    
    constructor(val) {
        this.val = val;
        this.next = null;
        this.prev = null;
    }
}

/* 基于双向链表实现的双向队列 */
class LinkedListDeque {
    front;  // 头结点 front
    rear;   // 尾结点 rear
    len;    // 双向队列的长度

    constructor() {
        this.front = null;
        this.rear = null;
        this.len = 0;
    }

    /* 队尾入队操作 */
    pushLast(val) {
        const node = new ListNode(val);
        // 若链表为空,则令 front, rear 都指向 node
        if (this.len === 0) {
            this.front = node;
            this.rear = node;
        } else {
            // 将 node 添加至链表尾部
            this.rear.next = node;
            node.prev = this.rear;
            this.rear = node; // 更新尾结点
        }
        this.len++;
    }

    /* 队首入队操作 */
    pushFirst(val) {
        const node = new ListNode(val);
        // 若链表为空,则令 front, rear 都指向 node
        if (this.len === 0) {
            this.front = node;
            this.rear = node;
        } else {
            // 将 node 添加至链表头部
            this.front.prev = node;
            node.next = this.front;
            this.front = node; // 更新头结点
        }
        this.len++;
    }

    /* 队尾出队操作 */
    pollLast() {
        if (this.len === 0) {
            return null;
        }
        const value = this.rear.val; // 存储尾结点值
        // 删除尾结点
        let temp = this.rear.prev;
        if (temp !== null) {
            temp.next = null;
            this.rear.prev = null;
        }
        this.rear = temp;   // 更新尾结点
        this.len--;
        return value;
    }

    /* 队首出队操作 */
    pollFirst() {
        if (this.len === 0) {
            return null;
        }
        const value = this.front.val; // 存储尾结点值
        // 删除头结点
        let temp = this.front.next;
        if (temp !== null) {
            temp.prev = null;
            this.front.next = null;
        }
        this.front = temp;   // 更新头结点
        this.len--;
        return value;
    }

    /* 访问队尾元素 */
    peekLast() {
        return this.len === 0 ? null : this.rear.val;
    }

    /* 访问队首元素 */
    peekFirst() {
        return this.len === 0 ? null : this.front.val;
    }

    /* 获取双向队列的长度 */
    size() {
        return this.len;
    }

    /* 判断双向队列是否为空 */
    isEmpty() {
        return this.len === 0;
    }

    /* 打印双向队列 */
    print() {
        const arr = [];
        let temp = this.front;
        while (temp !== null) {
            arr.push(temp.val);
            temp = temp.next;
        }
        console.log("[" + arr.join(", ") + "]");
    }
}
```

=== "TypeScript"

```typescript title="linkedlist_deque.ts"
/* 双向链表结点 */
class ListNode {
    prev: ListNode;     // 前驱结点引用 (指针)
    next: ListNode;     // 后继结点引用 (指针)
    val: number;        // 结点值

    constructor(val: number) {
        this.val = val;
        this.next = null;
        this.prev = null;
    }
}

/* 基于双向链表实现的双向队列 */
class LinkedListDeque {
    front: ListNode;    // 头结点 front
    rear: ListNode;     // 尾结点 rear
    len: number;        // 双向队列的长度

    constructor() {
        this.front = null;
        this.rear = null;
        this.len = 0;
    }
    
    /* 队尾入队操作 */
    pushLast(val: number): void {
        const node: ListNode = new ListNode(val);
        // 若链表为空,则令 front, rear 都指向 node
        if (this.len === 0) {
            this.front = node;
            this.rear = node;
        } else {
            // 将 node 添加至链表尾部
            this.rear.next = node;
            node.prev = this.rear;
            this.rear = node; // 更新尾结点
        }
        this.len++;
    }

    /* 队首入队操作 */
    pushFirst(val: number): void {
        const node: ListNode = new ListNode(val);
        // 若链表为空,则令 front, rear 都指向 node
        if (this.len === 0) {
            this.front = node;
            this.rear = node;
        } else {
            // 将 node 添加至链表头部
            this.front.prev = node;
            node.next = this.front;
            this.front = node; // 更新头结点
        }
        this.len++;
    }

    /* 队尾出队操作 */
    pollLast(): number {
        if (this.len === 0) {
            return null;
        }
        const value: number = this.rear.val; // 存储尾结点值
        // 删除尾结点
        let temp: ListNode = this.rear.prev;
        if (temp !== null) {
            temp.next = null;
            this.rear.prev = null;
        }
        this.rear = temp;   // 更新尾结点
        this.len--;
        return value;
    }

    /* 队首出队操作 */
    pollFirst(): number {
        if (this.len === 0) {
            return null;
        }
        const value: number = this.front.val; // 存储尾结点值
        // 删除头结点
        let temp: ListNode = this.front.next;
        if (temp !== null) {
            temp.prev = null;
            this.front.next = null;
        }
        this.front = temp;   // 更新头结点
        this.len--;
        return value;
    }

    /* 访问队尾元素 */
    peekLast(): number {
        return this.len === 0 ? null : this.rear.val;
    }

    /* 访问队首元素 */
    peekFirst(): number {
        return this.len === 0 ? null : this.front.val;
    }

    /* 获取双向队列的长度 */
    size(): number {
        return this.len;
    }

    /* 判断双向队列是否为空 */
    isEmpty(): boolean {
        return this.len === 0;
    }

    /* 打印双向队列 */
    print(): void {
        const arr: number[] = [];
        let temp: ListNode = this.front;
        while (temp !== null) {
            arr.push(temp.val);
            temp = temp.next;
        }
        console.log("[" + arr.join(", ") + "]");
    }
}
```

=== "C"

```c title="linkedlist_deque.c"

```

=== "C#"

```csharp title="linkedlist_deque.cs"

```

=== "Swift"

```swift title="linkedlist_deque.swift"

```

=== "Zig"

```zig title="linkedlist_deque.zig"

```