【题目链接】
【题目考点】
1. 链表(双向链表)
2. list
【解题思路】
解法1:手动实现双向链表
设数组na,na[i]
表示第i名同学对应的结点的地址。na[i]
为0表示第i名同学已被移去。
头插、尾插、插入函数都会返回插入结点的地址,将该地址保存在na数组中。
双向链表中的insert函数的作用为在地址it指向的元素前插入元素。若需在it指向的元素后插入,则应该先让it指向下一个元素,而后调用insert。
删除同学,直接调用删除结点的函数erase函数即可。
解法2:使用STL list
设数组na,类型为list<int>::iterator
,na[i]
表示指向第i名同学对应结点的迭代器。na[i]
为lis.end()表示第i名同学已被移去。
其它做法与解法1类似。
【题解代码】
解法1:手动实现双向链表
#include<bits/stdc++.h>
using namespace std;
#define N 100005
template<class T>
struct Node
{
T val;
int next, pre;
};
template<class T>
struct List
{
Node<T> node[N]; //内存池
int p; //当前可分配的元素的地址
int head; //头结点地址
int tail; //尾结点地址
List()
{
p = 0;
head = ++p;
tail = head;
}
int push_front(T d)//头插法 返回插入结点的地址
{
int np = ++p;
node[np].val = d;
node[np].next = node[head].next;
node[np].pre = head;
node[node[head].next].pre = np;//node[head].next为0,不影响
node[head].next = np;
if(tail == head)
tail = np;
return np;
}
int push_back(T d)//尾插法 返回插入结点的地址
{
int np = ++p;
node[np].val = d;
node[tail].next = np;
node[np].pre = tail;
tail = np;
return np;
}
int getAddr(int i)//获取第i个结点的地址(i从1开始数)
{
int ip = head; //第i个节点的地址
for(int j = 1; j <= i; j++)
ip = node[ip].next;
return ip;
}
int insert(int t, T d)//在t指向的结点前插入结点 返回插入的结点的地址
{
int np = ++p;
node[np].val = d;
node[np].next = t;
node[np].pre = node[t].pre;
node[node[t].pre].next = np;
node[t].pre = np;
return np;
}
int erase(int t)//删除t指向的结点 返回删除的结点的下一个结点的地址
{
node[node[t].pre].next = node[t].next;
node[node[t].next].pre = node[t].pre;
if(tail == t)//如果删掉的是尾结点
tail = node[t].pre; //把尾指针设为前一个结点
return node[t].next;
}
T get(int t)//获取t指向的结点的值
{
return node[t].val;
}
int begin()//第一个结点的地址
{
return node[head].next;
}
int end()//最后一个结点的下一个结点的地址
{
return 0;
}
int next(int t)//t指向结点下一个结点的地址
{
return node[t].next;
}
int pre(int t)//t指向结点前一个结点的地址
{
return node[t].pre;
}
};
List<int> li;
int pos[N];//pos[i]:第i号同学的地址
int main()
{
int n, k, p, t, np, m, x;
cin >> n;
pos[1] = li.push_front(1);//1号同学进队列
for(int i = 2; i <= n; ++i)
{
cin >> k >> p;
if(p == 0)//k左边插入
np = li.insert(pos[k], i);//在第k号同学结点地址pos[k]左边插入值i
else//k右边插入
{
if(li.next(pos[k]) == li.end())//如果k的下一个结点是end(),说明k是最后一个结点,需要用尾插法将i插到k右边
np = li.push_back(i);
else//k下一个结点的左边插入i
np = li.insert(li.next(pos[k]), i);
}
pos[i] = np;//记录第i号同学的结点地址是np
}
cin >> m;//m次删除
for(int i = 1; i <= m; ++i)
{
cin >> x;//删除x号同学
if(pos[x] > 0)//若pos[x]为0,则说明x号学生被移除了。避免重复删除。
{
li.erase(pos[x]);
pos[x] = 0;
}
}
for(int i = li.begin(); i != li.end(); i = li.next(i))//遍历链表 输出
cout << li.get(i) << ' ';
return 0;
}
解法2:
#include <bits/stdc++.h>
using namespace std;
typedef list<int>::iterator It;
#define N 100005
It na[N];//na[i]:指向表示第i同学结点的迭代器 na[i]为lis.end(),则表示第i同学已经不在队列之中
list<int> lis;
int main()
{
int n, k, p, t, m, x;
It ip;
cin >> n;
lis.push_back(1);//添加第1名同学
na[1] = lis.begin();
for(int i = 2; i <= n; ++i)
{
cin >> k >> p;
ip = na[k];//指向第k号同学的迭代器
if(p == 1)//如果在第k号同学右面插入
ip++;//那么ip加1后,在ip指向的结点左边插入
na[i] = lis.insert(ip, i);//在ip指向的结点左边插入值为i的新结点,指向该结点的迭代器保存在na[i]
}
cin >> m;
for(int i = 1; i <= m; ++i)
{
cin >> x;
if(na[x] != lis.end())//如果第x号同学还在队列内
{
lis.erase(na[x]);//删掉第x号同学
na[x] = lis.end();//na[x]设为lis.end()表示第x号同学已经不在队列内
}
}
for(It it = lis.begin(); it != lis.end(); it++)//遍历list,输出链表
cout << *it << ' ';
return 0;
}