洛谷-P1160 队列安排 题解

题目分析

这是一个关于队列构建和修改的问题。老师需要按照特定规则将N个同学排成一列,然后移除指定的M个同学,最后输出队列中剩余同学的编号。

解题思路

核心算法

使用单向链表来维护队列结构,通过维护一个指针数组来快速定位每个同学在链表中的前驱节点。

关键点

  1. 链表构建:每个新同学插入到已有同学的左边或右边
  2. 快速定位:使用数组arr记录每个编号对应的节点指针,实现O(1)时间复杂度的节点访问
  3. 删除操作:通过前驱节点直接修改指针关系,实现O(1)时间复杂度的删除

核心思想

链表 + 前驱指针数组的复合数据结构

关键思路分析

1. 数据结构选择

  • 使用单向链表:便于动态插入和删除操作
  • 维护前驱指针数组:arr[i]记录编号i的同学在链表中的前驱节点指针
  • 头节点技巧:使用哑节点简化边界条件处理

2. 删除操作优化

  • 通过前驱数组直接定位要删除节点的前驱
  • 修改指针关系实现O(1)删除
  • 标记已删除节点(设为nullptr)避免重复删除

3. 算法流程

  1. 初始化:建立头节点和1号同学
  2. 构建队列:依次插入2~N号同学
  3. 删除处理:移除指定同学,更新指针关系
  4. 结果输出:遍历链表输出剩余同学

算法复杂度分析

  • 时间复杂度:O(N + M)

构建队列:每个同学插入操作O(1),共O(N)

删除同学:每个删除操作O(1),共O(M)

输出结果:O(N)

  • 空间复杂度:O(N)链表节点存储:O(N)指针数组:O(N)

代码实现详解

#include<iostream>
#include<vector>using namespace std;
struct Node 
{
int val;        // 存储同学编号
Node* next;     // 指向下一个节点
Node(int v) : val(v), next(nullptr) {}Node() : val(0), next(nullptr) {}
};

vector<Node*> arr(100001, nullptr);  // 指针数组,记录每个编号对应的节点前驱

int main() 
{
// 初始化链表,创建头节点和1号同学
Node* head = new Node(0);
head->next = new Node(1);arr[1] = head;  // 1号同学的前驱是头节点
int n, m;
cin >> n;
int index, flag;
Node* tmp;

// 构建队列:2~n号同学依次插入
for (int i = 2; i <= n; i++) {
    Node* cur = new Node(i);  // 创建新同学节点
    cin >> index >> flag;     // 读取插入位置和方向
    
    tmp = arr[index];  // 找到目标同学的前驱
    
    if (flag) {  // 插入到右边
        tmp = tmp->next;  // 移动到目标同学本身
    }
    // 此时tmp指向新同学应该插入位置的前一个节点
    
    arr[i] = tmp;  // 记录新同学的前驱
    
    if (tmp->next)  // 如果tmp后面有同学,更新其后继的前驱指针
        arr[tmp->next->val] = cur;
        
    // 执行插入操作
    cur->next = tmp->next;
    tmp->next = cur;
}

// 删除操作
cin >> m;
for (int i = 0; i < m; i++) {
    cin >> index;
    tmp = arr[index];  // 找到要删除同学的前驱
    
    if (tmp && tmp->next) {  // 如果同学存在且未被删除
        arr[tmp->next->val] = nullptr;  // 标记为已删除
        
        if (tmp->next->next)  // 更新后继同学的前驱指针
            arr[tmp->next->next->val] = tmp;
            
        tmp->next = tmp->next->next;  // 从链表中移除
    }
}

// 输出结果
tmp = head->next;
while (tmp) {
    cout << tmp->val << ' ';
    tmp = tmp->next;
}

return 0;
}
全部评论

相关推荐

1️⃣排序与选择·快排(&nbsp;LC&nbsp;912)·数组中第&nbsp;k&nbsp;大的元素(&nbsp;LC&nbsp;215)·数组中最小的&nbsp;k&nbsp;个数(&nbsp;LC&nbsp;面试题17.14)2️⃣二分与数学(含概率)·&nbsp;sqrt&nbsp;(&nbsp;x&nbsp;)(&nbsp;LC&nbsp;69)·pow&nbsp;(&nbsp;x&nbsp;,&nbsp;n&nbsp;)(&nbsp;LC&nbsp;50)·搜索旋转数组(&nbsp;LC&nbsp;33)·Rand7实现Rand10(&nbsp;LC&nbsp;470)3️⃣双指针与滑动窗口·三数之和(&nbsp;LC&nbsp;15)·滑动窗口最大值(&nbsp;LC&nbsp;239)·有效三角形的个数(&nbsp;LC&nbsp;611)·最小覆盖子串(&nbsp;LC&nbsp;76)·长度最小子数组(&nbsp;LC&nbsp;209)4️⃣栈与队列/表达式·有效的括号(&nbsp;LC&nbsp;20)·最长有效括号(&nbsp;LC&nbsp;32)·逆波兰表达式求值(&nbsp;LCR&nbsp;036)5️⃣链表·反转链表(&nbsp;LC&nbsp;206)·反转链表&nbsp;II&nbsp;(&nbsp;LC&nbsp;92)·k&nbsp;个一组翻转链表(&nbsp;LC&nbsp;25)·环形链表/环形链表&nbsp;II&nbsp;(&nbsp;LC&nbsp;141/142)·删除链表倒数第&nbsp;n&nbsp;个节点(&nbsp;LC&nbsp;19)·课程表&nbsp;II&nbsp;(&nbsp;LC&nbsp;210)6️⃣动态规划(序列/路径/计数/区间)·最大子数组和(&nbsp;LC&nbsp;53)·最长递增子序列&nbsp;LIS&nbsp;(&nbsp;LC&nbsp;300)·最小路径和(&nbsp;LC&nbsp;64)·加油站(贪心/&nbsp;DP&nbsp;,&nbsp;LC&nbsp;134)·最大乘积子数组(&nbsp;LC&nbsp;152)·打家劫舍&nbsp;II&nbsp;(&nbsp;LC&nbsp;213)·不同的子序列(&nbsp;LC&nbsp;115)·爬楼梯(&nbsp;LC&nbsp;70)·最长公共子序列&nbsp;LCS&nbsp;(&nbsp;LC&nbsp;1143)7️⃣字符串·最长回文子串(&nbsp;LC&nbsp;5)·最长回文子序列(&nbsp;LC&nbsp;516)·字符串解码(&nbsp;LC&nbsp;394)·编辑距离(&nbsp;LC&nbsp;72)·大数相乘(&nbsp;LC&nbsp;43)
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务