C++ STL two-way queue deque detailed explanation

Part.I Attention

insert image description here

  • Deque is the abbreviation of double-ended queue, which means double-ended queue. For details, see the official website .
  • deque<T>The container adapter is
  • header file must be included#include <deque>
  • dequeCompared with vector: ① it is not good at adding and deleting elements in the middle; ② it is good at adding and deleting elements at the head and tail of the queue, the time complexity is O(1), and vectoronly the time complexity of adding and deleting elements at the tail is O(1).
  • dequeCompared with queue: queue is only good at deleting elements at the head and adding elements at the end; while deque is good at adding or removing elements at both the head and the tail.
  • The C++ two-way queue is not used a lot, and it is generally used when brushing questions vector. But it is also a typical data structure in STL, so it can also be considered in actual production.

Part.II Funciton

dequeThe function of is queuemuch more than that of , and Vectoris equivalent, as shown in the following figure:

insert image description here

The following is a description of commonly used functions:

function explain
begin() start iterator
end() end iterator
rbegin() iterator starting in reverse
rend() iterator with reverse end
size() Contains the number of elements
max_size The maximum number of elements that can be accommodated
resize() resize it
empty() Check if it is empty
front() frontmost element
back() last element
assign() It can be assigned to it through an array
push_back() Add an element to the end of the deque
push_front() Add an element to the front of the deque
pop_back() Pop the last element of the double-ended queue
pop_front() Pop the first element of the double-ended queue
insert() Insert elements, there are several usages
erase(d.begin(),d.begin()+3) delete the first 3 elements
a.swap(b) exchange aandb
clear() clear all elements
emplace(itr,a) itrInsert an element before the iterator position a, the returned aiterator
emplace_front() insert element before
emplace_back() insert element after

Part.III Code

The following introduces the use of double -ended queues based on a programming question on LeetCode Sword Pointer Offer II 026. Rearrange the linked list .

Chap.I topic description and analysis

topic description

Given the head node head of a singly linked list L, the singly linked list L is expressed as:
L0 → L1 → … → Ln-1 → Ln

Please rearrange it to become:
L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …

You can't just change the value inside the node, but you need to actually exchange the node.


analyze

According to the meaning of the question, the original linked list needs to be rearranged in a staggered form; the author immediately thought of a double-ended queue, and the idea is as follows:

  • First traverse the original linked list on one side, and store all nodes in the double-ended queue
  • Then take one from the head and one from the tail in sequence
  • until there are no more nodes in the queue.
  • time complexity O(n), space complexityO(n)

Chap.II code implementation

Deque C++code.

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    void reorderList(ListNode* head) {
    
    
        deque<ListNode*> dqu;
        while(head) {
    
    
            dqu.emplace_back(head);
            head=head->next;
        }
        ListNode* pre=nullptr;
        while(dqu.size()>=2) {
    
    
            if(!pre) {
    
    
                pre=dqu.front();
            }
            else {
    
    
                pre->next=dqu.front();
                pre=pre->next;
            }
            pre->next=dqu.back();
            pre=pre->next;
            dqu.pop_front();dqu.pop_back();
        }
        if(!dqu.empty()) {
    
    
            if(!pre) pre=dqu.front();
            else {
    
    
                pre->next=dqu.front();
                pre=pre->next;
            }
        }
        pre->next=nullptr;
    }
};

It can be used as an example of using a double-ended queue, but for this question, using a double-ended queue is not necessarily the optimal solution.

Chap.III Two ways of solving problems

The following way of thinking is more efficient:

  • Use two pointers, one is a fast pointer (two nodes at a time), and the other is a slow pointer (one node at a time); the slow pointer flips the linked list while walking, and uses one as an preintermediate variable.
  • When all the fast pointers reach the end of the linked list, the fast pointer just points to the middle of the linked list
  • At this time, the fast pointer is pointing pre, and the slow pointer continues to move forward, and the results can be crossed while walking.

The following is the code implementation (it is a bit messy to write)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    void reorderList(ListNode* head) {
    
    
        if(!head||!head->next) return;
        int tag=0;      // 针对节点个数奇偶涉及的标识 奇1偶0
        ListNode* slow=head, *fast=head, *pre=nullptr;
        while(fast) {
    
    
            if(fast->next) {
    
    
                fast=fast->next;
                ListNode* tmp=slow->next;
                slow->next=pre; pre=slow;
                slow=tmp;
            }
            else {
    
     tag=1; break; }
            if(fast->next) {
    
    
                fast=fast->next;
            }
            else break;
        }
        fast=nullptr;
        head=slow; // slow 就是中点
        slow=slow->next;
        head->next=fast;
        if(tag) {
    
     fast=slow; slow=slow->next; fast->next=head; head=fast; }
        while(slow) {
    
    
            fast=pre; pre=pre->next; fast->next=head; head=fast;
            fast=slow; slow=slow->next; fast->next=head; head=fast;
        }
        if(pre) pre->next=head; head=pre;
    }
};

Implementation of Part.IV double-ended queue

The following part is the underlying implementation idea of ​​the double-ended queue, taking the topic of LeetCode 641. Designing a circular double-ended queue as an example.

Chap.I Title Description

Under the premise of not using dequeclasses, the design implements double-ended queues.

Implement the MyCircularDeque class:

  • MyCircularDeque(int k): Constructor, the double-ended queue is at most k.
  • boolean insertFront(): Add an element to the head of the deque. Returns true if the operation was successful, false otherwise.
  • boolean insertLast(): Add an element to the tail of the double-ended queue. Returns true if the operation was successful, false otherwise.
  • boolean deleteFront(): Remove an element from the head of the double-ended queue. Returns true if the operation was successful, false otherwise.
  • boolean deleteLast(): Remove an element from the tail of the double-ended queue. Returns true if the operation was successful, false otherwise.
  • int getFront(): Get an element from the head of the double-ended queue. Returns -1 if the deque is empty.
  • int getRear(): Get the last element of the double-ended queue. Returns -1 if the deque is empty.
  • boolean isEmpty(): Returns true if the double-ended queue is empty, otherwise returns false.
  • boolean isFull(): If the double-ended queue is full, return true, otherwise return false.

Chap.II code implementation

This question is very similar to 662. Designing a Circular Queue . You can see the solution

#include <iostream>
#include <vector>

using namespace std;

class MyCircularDeque {
    
    

private:
    vector<int> arr;
    int front;
    int rear;
    int capacity;

public:
    /** Initialize your data structure here. Set the size of the deque to be k. */
    MyCircularDeque(int k) {
    
    
        capacity = k + 1;
        arr.assign(capacity, 0);

        front = 0;
        rear = 0;
    }

    /** Adds an item at the front of Deque. Return true if the operation is successful. */
    bool insertFront(int value) {
    
    
        if (isFull()) {
    
    
            return false;
        }
        front = (front - 1 + capacity) % capacity;
        arr[front] = value;
        return true;
    }

    /** Adds an item at the rear of Deque. Return true if the operation is successful. */
    bool insertLast(int value) {
    
    
        if (isFull()) {
    
    
            return false;
        }
        arr[rear] = value;
        rear = (rear + 1) % capacity;
        return true;
    }

    /** Deletes an item from the front of Deque. Return true if the operation is successful. */
    bool deleteFront() {
    
    
        if (isEmpty()) {
    
    
            return false;
        }
        // front 被设计在数组的开头,所以是 +1
        front = (front + 1) % capacity;
        return true;
    }

    /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
    bool deleteLast() {
    
    
        if (isEmpty()) {
    
    
            return false;
        }
        // rear 被设计在数组的末尾,所以是 -1
        rear = (rear - 1 + capacity) % capacity;
        return true;
    }

    /** Get the front item from the deque. */
    int getFront() {
    
    
        if (isEmpty()) {
    
    
            return -1;
        }
        return arr[front];
    }

    /** Get the last item from the deque. */
    int getRear() {
    
    
        if (isEmpty()) {
    
    
            return -1;
        }
        // 当 rear 为 0 时防止数组越界
        return arr[(rear - 1 + capacity) % capacity];
    }

    /** Checks whether the circular deque is empty or not. */
    bool isEmpty() {
    
    
        return front == rear;
    }

    /** Checks whether the circular deque is full or not. */
    bool isFull() {
    
    
        // 注意:这个设计是非常经典的做法
        return (rear + 1) % capacity == front;
    }
};

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque* obj = new MyCircularDeque(k);
 * bool param_1 = obj->insertFront(value);
 * bool param_2 = obj->insertLast(value);
 * bool param_3 = obj->deleteFront();
 * bool param_4 = obj->deleteLast();
 * int param_5 = obj->getFront();
 * int param_6 = obj->getRear();
 * bool param_7 = obj->isEmpty();
 * bool param_8 = obj->isFull();
 */

Guess you like

Origin blog.csdn.net/Gou_Hailong/article/details/128381718