数据结构 学习 队列 2025年6月14日 11点22分

循环队列

循环队列是一种线性数据结构,它遵循FIFO(先进先出)原则,但与普通队列不同的是,循环队列的最后一个元素连接回第一个元素,形成一个环形结构。这种设计有效解决了普通队列的"假溢出"问题,可以更高效地利用存储空间。

基本概念

1. 循环队列特点

  • 环形结构:队尾连接队首,形成循环

  • 高效空间利用:重用出队元素释放的空间

  • 两个指针:front(队首)和rear(队尾)

  • 判空判满:需要特殊处理区分队列空和满的状态

2. 基本操作

  • Enqueue:向队尾添加元素

  • Dequeue:从队首移除元素

  • Front:获取队首元素

  • Rear:获取队尾元素

  • isEmpty:判断队列是否为空

  • isFull:判断队列是否已满

实际应用
  1. CPU任务调度:循环分配CPU时间片

  2. 内存管理:循环缓冲区处理数据流

  3. 网络数据包处理:按顺序处理到达的数据包

  4. 打印机队列:管理多个打印任务

  5. 音乐播放列表:循环播放歌曲

复制代码
//数组实现(固定大小)
class CircularQueue {
private:
    vector<int> data;
    int front;
    int rear;
    int size;
    
public:
    CircularQueue(int k) {
        data.resize(k);
        front = -1;
        rear = -1;
        size = k;
    }
    
    bool enQueue(int value) {
        if (isFull()) return false;
        if (isEmpty()) front = 0;
        rear = (rear + 1) % size;
        data[rear] = value;
        return true;
    }
    
    bool deQueue() {
        if (isEmpty()) return false;
        if (front == rear) {
            front = -1;
            rear = -1;
        } else {
            front = (front + 1) % size;
        }
        return true;
    }
    
    int Front() {
        if (isEmpty()) return -1;
        return data[front];
    }
    
    int Rear() {
        if (isEmpty()) return -1;
        return data[rear];
    }
    
    bool isEmpty() {
        return front == -1;
    }
    
    bool isFull() {
        return (rear + 1) % size == front;
    }
};
复制代码
//链表实现
class Node {
public:
    int val;
    Node* next;
    Node(int value) : val(value), next(nullptr) {}
};

class LinkedCircularQueue {
private:
    Node *front, *rear;
    
public:
    LinkedCircularQueue() : front(nullptr), rear(nullptr) {}
    
    bool enQueue(int value) {
        Node* newNode = new Node(value);
        if (rear == nullptr) {
            front = rear = newNode;
        } else {
            rear->next = newNode;
            rear = newNode;
        }
        rear->next = front; // 形成循环
        return true;
    }
    
    bool deQueue() {
        if (isEmpty()) return false;
        int value = front->val;
        Node* temp = front;
        if (front == rear) {
            front = rear = nullptr;
        } else {
            front = front->next;
            rear->next = front; // 保持循环
        }
        delete temp;
        return true;
    }
    
    int Front() {
        if (isEmpty()) return -1;
        return front->val;
    }
    
    int Rear() {
        if (isEmpty()) return -1;
        return rear->val;
    }
    
    bool isEmpty() {
        return front == nullptr;
    }
    
    // 链表实现通常不考虑满的情况(除非内存耗尽)
    bool isFull() {
        return false; 
    }
};
示例
复制代码
#include <iostream>
#include <vector>

using namespace std;

class CircularQueue {
private:
    vector<int> data;  // 使用vector存储队列元素
    int front;         // 队首指针
    int rear;          // 队尾指针
    int size;          // 队列容量
    
public:
    // 构造函数,初始化队列容量
    CircularQueue(int k) {
        data.resize(k); // 分配存储空间
        front = -1;     // 初始时队首指针为-1表示空队列
        rear = -1;      // 初始时队尾指针为-1表示空队列
        size = k;       // 设置队列容量
    }
    
    // 入队操作
    bool enQueue(int value) {
        if (isFull()) {  // 检查队列是否已满
            cout << "队列已满,无法插入 " << value << endl;
            return false;
        }
        if (isEmpty()) { // 如果是第一个元素
            front = 0;   // 初始化队首指针
        }
        rear = (rear + 1) % size; // 循环移动队尾指针
        data[rear] = value;       // 存储元素
        cout << "插入 " << value << " 成功" << endl;
        return true;
    }
    
    // 出队操作
    bool deQueue() {
        if (isEmpty()) {  // 检查队列是否为空
            cout << "队列为空,无法删除" << endl;
            return false;
        }
        int value = data[front]; // 获取队首元素
        if (front == rear) {     // 如果队列中只有一个元素
            front = -1;          // 重置队首指针
            rear = -1;           // 重置队尾指针
        } else {
            front = (front + 1) % size; // 循环移动队首指针
        }
        cout << "删除 " << value << " 成功" << endl;
        return true;
    }
    
    // 获取队首元素
    int Front() {
        if (isEmpty()) return -1; // 队列为空返回-1
        return data[front];       // 返回队首元素
    }
    
    // 获取队尾元素
    int Rear() {
        if (isEmpty()) return -1; // 队列为空返回-1
        return data[rear];        // 返回队尾元素
    }
    
    // 判断队列是否为空
    bool isEmpty() {
        return front == -1; // 队首指针为-1表示空队列
    }
    
    // 判断队列是否已满
    bool isFull() {
        return (rear + 1) % size == front; // 队尾下一个位置是队首表示队列已满
    }
    
    // 显示队列内容
    void display() {
        if (isEmpty()) {  // 检查队列是否为空
            cout << "队列为空" << endl;
            return;
        }
        cout << "队列元素: ";
        if (rear >= front) {  // 队列元素没有跨越数组边界
            for (int i = front; i <= rear; i++)
                cout << data[i] << " ";
        } else {  // 队列元素跨越数组边界(循环情况)
            for (int i = front; i < size; i++)  // 打印队首到数组末尾的元素
                cout << data[i] << " ";
            for (int i = 0; i <= rear; i++)    // 打印数组开头到队尾的元素
                cout << data[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    // 创建容量为5的循环队列
    CircularQueue q(5);
    
    // 入队操作测试
    q.enQueue(1);
    q.enQueue(2);
    q.enQueue(3);
    q.enQueue(4);
    q.enQueue(5);
    q.enQueue(6); // 队列已满,无法插入
    
    // 显示队列内容
    q.display();
    
    // 出队操作测试
    q.deQueue();
    q.deQueue();
    
    // 显示队列内容
    q.display();
    
    // 继续入队测试循环特性
    q.enQueue(6);
    q.enQueue(7);
    
    // 显示队列内容
    q.display();
    
    // 获取队首和队尾元素
    cout << "队首元素: " << q.Front() << endl;
    cout << "队尾元素: " << q.Rear() << endl;
    
    return 0;
}

双向队列

双向队列是一种非常实用的数据结构,它提供了比普通队列和栈更灵活的操作方式,在算法设计和系统开发中都有广泛应用。

一种允许在两端进行插入和删除操作的线性数据结构。它结合了栈和队列的特性,提供了更灵活的数据操作方式。

基本概念
1. 双向队列特点
  • 双端操作:可以在队首和队尾进行插入和删除

  • 灵活性强:既可以作为队列使用(FIFO),也可以作为栈使用(LIFO)

  • 多种实现方式:可以使用数组或链表实现

2. 基本操作
  • push_front:在队首插入元素

  • push_back:在队尾插入元素

  • pop_front:删除队首元素

  • pop_back:删除队尾元素

  • front:获取队首元素

  • back:获取队尾元素

  • size:获取元素数量

  • empty:判断是否为空

复制代码
//基与双链表实现

#include <iostream>
using namespace std;

// 双向链表节点
struct Node {
    int data;
    Node* prev;
    Node* next;
    Node(int val) : data(val), prev(nullptr), next(nullptr) {}
};

class Deque {
private:
    Node* front;
    Node* rear;
    int count;

public:
    Deque() : front(nullptr), rear(nullptr), count(0) {}
    
    ~Deque() {
        while (!empty()) {
            pop_front();
        }
    }

    // 在队首插入
    void push_front(int val) {
        Node* newNode = new Node(val);
        if (empty()) {
            front = rear = newNode;
        } else {
            newNode->next = front;
            front->prev = newNode;
            front = newNode;
        }
        count++;
    }

    // 在队尾插入
    void push_back(int val) {
        Node* newNode = new Node(val);
        if (empty()) {
            front = rear = newNode;
        } else {
            newNode->prev = rear;
            rear->next = newNode;
            rear = newNode;
        }
        count++;
    }

    // 删除队首元素
    void pop_front() {
        if (empty()) {
            cout << "Deque is empty!" << endl;
            return;
        }
        Node* temp = front;
        front = front->next;
        if (front == nullptr) {
            rear = nullptr;
        } else {
            front->prev = nullptr;
        }
        delete temp;
        count--;
    }

    // 删除队尾元素
    void pop_back() {
        if (empty()) {
            cout << "Deque is empty!" << endl;
            return;
        }
        Node* temp = rear;
        rear = rear->prev;
        if (rear == nullptr) {
            front = nullptr;
        } else {
            rear->next = nullptr;
        }
        delete temp;
        count--;
    }

    // 获取队首元素
    int get_front() {
        if (empty()) {
            cout << "Deque is empty!" << endl;
            return -1;
        }
        return front->data;
    }

    // 获取队尾元素
    int get_back() {
        if (empty()) {
            cout << "Deque is empty!" << endl;
            return -1;
        }
        return rear->data;
    }

    // 获取元素数量
    int size() {
        return count;
    }

    // 判断是否为空
    bool empty() {
        return count == 0;
    }

    // 打印队列内容
    void display() {
        Node* current = front;
        cout << "Deque: [";
        while (current != nullptr) {
            cout << current->data;
            if (current->next != nullptr) {
                cout << ", ";
            }
            current = current->next;
        }
        cout << "]" << endl;
    }
};

//基于循环数组的实现
#include <iostream>
#include <vector>
using namespace std;

class ArrayDeque {
private:
    vector<int> data;
    int front;
    int rear;
    int capacity;
    int count;

public:
    ArrayDeque(int k) : capacity(k), front(0), rear(0), count(0) {
        data.resize(k);
    }

    // 在队首插入
    bool push_front(int val) {
        if (isFull()) {
            cout << "Deque is full!" << endl;
            return false;
        }
        front = (front - 1 + capacity) % capacity;
        data[front] = val;
        count++;
        return true;
    }

    // 在队尾插入
    bool push_back(int val) {
        if (isFull()) {
            cout << "Deque is full!" << endl;
            return false;
        }
        data[rear] = val;
        rear = (rear + 1) % capacity;
        count++;
        return true;
    }

    // 删除队首元素
    bool pop_front() {
        if (isEmpty()) {
            cout << "Deque is empty!" << endl;
            return false;
        }
        front = (front + 1) % capacity;
        count--;
        return true;
    }

    // 删除队尾元素
    bool pop_back() {
        if (isEmpty()) {
            cout << "Deque is empty!" << endl;
            return false;
        }
        rear = (rear - 1 + capacity) % capacity;
        count--;
        return true;
    }

    // 获取队首元素
    int get_front() {
        if (isEmpty()) {
            cout << "Deque is empty!" << endl;
            return -1;
        }
        return data[front];
    }

    // 获取队尾元素
    int get_back() {
        if (isEmpty()) {
            cout << "Deque is empty!" << endl;
            return -1;
        }
        return data[(rear - 1 + capacity) % capacity];
    }

    // 判断是否为空
    bool isEmpty() {
        return count == 0;
    }

    // 判断是否已满
    bool isFull() {
        return count == capacity;
    }

    // 获取元素数量
    int size() {
        return count;
    }

    // 打印队列内容
    void display() {
        cout << "Deque: [";
        for (int i = 0; i < count; i++) {
            cout << data[(front + i) % capacity];
            if (i < count - 1) {
                cout << ", ";
            }
        }
        cout << "]" << endl;
    }
};
实际应用
  1. 撤销操作:许多编辑器使用双向队列实现撤销功能

  2. 滑动窗口:解决滑动窗口最大值等问题

  3. 任务调度:操作系统中的任务调度算法

  4. 浏览器历史记录:前进和后退功能

  5. 回文检查:可以从两端同时检查字符

示例代码
复制代码
int main() {
    // 测试链表实现的Deque
    cout << "Linked List Deque:" << endl;
    Deque dq;
    dq.push_back(10);
    dq.push_back(20);
    dq.push_front(5);
    dq.display(); // [5, 10, 20]
    
    cout << "Front: " << dq.get_front() << endl; // 5
    cout << "Back: " << dq.get_back() << endl;   // 20
    
    dq.pop_front();
    dq.display(); // [10, 20]
    
    dq.pop_back();
    dq.display(); // [10]
    
    // 测试数组实现的Deque
    cout << "\nArray Deque:" << endl;
    ArrayDeque adq(5);
    adq.push_back(100);
    adq.push_front(50);
    adq.push_back(200);
    adq.display(); // [50, 100, 200]
    
    cout << "Front: " << adq.get_front() << endl; // 50
    cout << "Back: " << adq.get_back() << endl;   // 200
    
    adq.pop_front();
    adq.display(); // [100, 200]
    
    adq.pop_back();
    adq.display(); // [100]
    
    return 0;
}

单调队列

单调队列是一种特殊的队列数据结构,它保持队列中元素的单调性(单调递增或单调递减)。这种数据结构常用于解决滑动窗口相关问题,能够高效地获取窗口中的最大值或最小值。

单调队列通过维护数据的单调性,将原本O(nk)的滑动窗口问题优化到O(n),是解决一类极值问题的有效工具。掌握其核心思想和实现技巧对算法能力提升有很大帮助

基本概念
1. 单调队列特性
  • 单调性:队列中元素保持单调递增或单调递减

  • 双端操作:可以从队首和队尾进行插入和删除

  • 高效性:能在O(1)时间内获取当前窗口的最大值/最小值

2. 常见应用场景
  • 滑动窗口最大值/最小值

  • 股票价格分析

  • 数据流中的极值问题

  • 动态规划优化

复制代码
//单调递减队列 用于求滑动窗口最大值)

#include <deque>
#include <vector>

using namespace std;

class MonotonicQueue {
private:
    deque<int> data; // 存储元素的下标

public:
    // 在队尾添加元素,维护单调递减性
    void push(int idx, const vector<int>& nums) {
        while (!data.empty() && nums[data.back()] <= nums[idx]) {
            data.pop_back(); // 移除比当前元素小的元素
        }
        data.push_back(idx);
    }

    // 获取当前队列最大值(队首元素)
    int max() {
        return data.front();
    }

    // 移除超出窗口范围的元素
    void pop(int idx) {
        while (!data.empty() && data.front() <= idx) {
            data.pop_front();
        }
    }

    // 判断队列是否为空
    bool empty() {
        return data.empty();
    }
};

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    vector<int> result;
    MonotonicQueue mq;
    
    // 初始化第一个窗口
    for (int i = 0; i < k; ++i) {
        mq.push(i, nums);
    }
    result.push_back(nums[mq.max()]);
    
    // 滑动窗口
    for (int i = k; i < nums.size(); ++i) {
        mq.pop(i - k); // 移除离开窗口的元素
        mq.push(i, nums); // 添加新元素
        result.push_back(nums[mq.max()]);
    }
    
    return result;
}

//单调递增队列 用于求滑动窗口最小值)
class MonotonicQueueMin {
private:
    deque<int> data; // 存储元素的下标

public:
    void push(int idx, const vector<int>& nums) {
        while (!data.empty() && nums[data.back()] >= nums[idx]) {
            data.pop_back(); // 移除比当前元素大的元素
        }
        data.push_back(idx);
    }

    int min() {
        return data.front();
    }

    void pop(int idx) {
        while (!data.empty() && data.front() <= idx) {
            data.pop_front();
        }
    }

    bool empty() {
        return data.empty();
    }
};

vector<int> minSlidingWindow(vector<int>& nums, int k) {
    vector<int> result;
    MonotonicQueueMin mq;
    
    for (int i = 0; i < k; ++i) {
        mq.push(i, nums);
    }
    result.push_back(nums[mq.min()]);
    
    for (int i = k; i < nums.size(); ++i) {
        mq.pop(i - k);
        mq.push(i, nums);
        result.push_back(nums[mq.min()]);
    }
    
    return result;
}
经典应用
复制代码
//滑动窗口最大值
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    vector<int> res;
    deque<int> dq; // 存储下标
    
    for (int i = 0; i < nums.size(); ++i) {
        // 移除超出窗口范围的元素
        while (!dq.empty() && dq.front() <= i - k) {
            dq.pop_front();
        }
        
        // 维护单调递减性
        while (!dq.empty() && nums[dq.back()] <= nums[i]) {
            dq.pop_back();
        }
        
        dq.push_back(i);
        
        // 窗口形成后开始记录结果
        if (i >= k - 1) {
            res.push_back(nums[dq.front()]);
        }
    }
    
    return res;
}

//队列的最大值
 class MaxQueue {
private:
    queue<int> data;
    deque<int> max_q;

public:
    MaxQueue() {}
    
    int max_value() {
        if (max_q.empty()) return -1;
        return max_q.front();
    }
    
    void push_back(int value) {
        data.push(value);
        while (!max_q.empty() && max_q.back() < value) {
            max_q.pop_back();
        }
        max_q.push_back(value);
    }
    
    int pop_front() {
        if (data.empty()) return -1;
        int val = data.front();
        data.pop();
        if (val == max_q.front()) {
            max_q.pop_front();
        }
        return val;
    }
};

//股票的价格跨度
class StockSpanner {
private:
    stack<pair<int, int>> st; // {price, span}
    
public:
    StockSpanner() {}
    
    int next(int price) {
        int span = 1;
        while (!st.empty() && st.top().first <= price) {
            span += st.top().second;
            st.pop();
        }
        st.push({price, span});
        return span;
    }
};
相关推荐
fengye20716123 分钟前
板凳-------Mysql cookbook学习 (十--7)
数据库·学习·mysql
Zephyrtoria2 小时前
区间合并:区间合并问题
java·开发语言·数据结构·算法
uyeonashi5 小时前
【QT】窗口详解
开发语言·c++·qt·学习
Hello eveybody6 小时前
C++介绍整数二分与实数二分
开发语言·数据结构·c++·算法
囚生CY6 小时前
【学习笔记】Langchain基础(二)
笔记·学习·langchain
Jay_5157 小时前
C语言环形数组(循环队列)详解:原理、实现与应用
c语言·学习·嵌入式·环形数组
Jay Kay7 小时前
TensorFlow Serving学习笔记2: 模型服务
学习·tensorflow
GalaxyPokemon8 小时前
LeetCode - 704. 二分查找
数据结构·算法·leetcode
GISDance9 小时前
2025年高考志愿填报指导资料
学习·考研·高考