双向静态链表(数组实现)(洛谷P1160 队列安排)
题目
见题目链接
题目链接队列安排 - 洛谷
输入
见题目链接
输出
见题目链接
样例
见题目链接
题解
此题涉及大量数据的移动、删除,因此首先想到要使用链表,但动态链表(指针实现)有一个缺点:查询某一个节点的效率低O(n),在此题的数据范围下是不可行的。然而查询某个节点正是数组的强项,效率为O(1),非常适合本题,因此我们可以构造一个用数组实现的静态链表,数组的下标即为学生的编号。
代码
#include <stdio.h>
#include <stdlib.h>
typedef struct student{
int left,right,flag;
}LinkList;
int main(){
LinkList stu[100005];
for (int i = 0 ; i < 100005; i++){
stu[i].flag = 0;
stu[i].left = 0;
stu[i].right = 0;
}
int n,m,point;
stu[1].left = 0;
stu[1].right = 0;
stu[1].flag = 1;
point = 1;
scanf("%d", &n);
for (int i = 0; i < n-1;i++)
{
int k,p;
scanf("%d %d", &k, &p);
if (p == 0){
if (stu[k].left != 0){
stu[i+2].left = stu[k].left;
stu[stu[k].left].right = i+2;
}
else
stu[i+2].left = 0;
stu[i+2].right = k;
stu[k].left = i+2;
stu[i+2].flag = 1;
if (k == point){
point = i+2;
}
}
else{
if (stu[k].right != 0){
stu[i+2].right = stu[k].right;
stu[stu[k].right].left = i+2;
}
else
stu[i+2].right = 0;
stu[i+2].left = k;
stu[k].right = i+2;
stu[i+2].flag = 1;
}
}
scanf("%d", &m);
for (int i = 0; i < m; i++){
int x;
scanf("%d", &x);
if (stu[x].flag){
stu[stu[x].left].right = stu[x].right;
stu[stu[x].right].left = stu[x].left;
stu[x].flag = 0;
}
}
do {
printf("%d ", point);
point = stu[point].right;
} while (point != 0);
return 0;
}
代码解释
链表定义(结构体):
typedef struct student{
int left,right,flag;
}LinkList;
初始化链表:
LinkList stu[100005];
for (int i = 0 ; i < 100005; i++){
stu[i].flag = 0;
stu[i].left = 0;
stu[i].right = 0;
}
对第一个节点进行初始化:
stu[1].left = 0;
stu[1].right = 0;
stu[1].flag = 1;
point = 1;
构造链表(重点!!!):
for (int I = 0; I < n-1;i++)
{
int k,p;
scanf(“%d %d”, &k, &p);
If (p == 0){ //从原节点的左侧插入
if (stu[k].left != 0){
stu[i+2].left = stu[k].left;
stu[stu[k].left].right = I+2;
} //如果原节点左侧有节点,则需要更改插入节点的左指针和原节点左侧节点的右指针
else
stu[i+2].left = 0; //如果原节点左侧没有节点,则插入节点的左指针设为0
stu[i+2].right = k; //将插入节点的右指针指向原节点
stu[k].left = I+2; //将原节点的左指针指向插入节点
stu[i+2].flag = 1; //设置标记,表示此节点已经在链表中
if (k == point){
Point = I+2; //point用来记录链表此时最左端的节点
}
}
Else{ //从原节点的右侧插入,与从左侧插入类似,不再赘述
if (stu[k].right != 0){
stu[i+2].right = stu[k].right;
stu[stu[k].right].left = i+2;
}
else
stu[i+2].right = 0;
stu[i+2].left = k;
stu[k].right = i+2;
stu[i+2].flag = 1;
}
}
从链表中删去指定的节点:
for (int I = 0; I < m; I++){
int x;
scanf(“%d”, &x);
if (stu[x].flag){
stu[stu[x].left].right = stu[x].right; //将被删节点左指针指向的节点的右指针指向被删节点的右指针指向的元素
stu[stu[x].right].left = stu[x].left; //将被删节点有指针指向的节点的左指针指向被删节点的左指针指向的元素
stu[x].flag = 0;
}
}
从链表的最左端找起,输出各个节点:
do {
printf(“%d “, point);
point = stu[point].right;
} while (point != 0);