洛谷:P1160 队列安排(彻底掌握线性表)

题解:

洛谷:P1160 队列安排(彻底掌握线性表)_第1张图片

题目来源:P1160 队列安排 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

1.对于这题我们用暴力解法,也就正常的对数据插入删除,时间复杂度肯定不言而喻,所以我们需要对自己的代码稍加改进。

我们知道对于链表,我们查找非常耗时,特别对于一个很长的数据,要找到后面的数据,那是非常耗时,但找到数据之后,链表的删除操作速度就非常快了。而对于顺序表,也就是我们常谈的数组,我们可以直接通过下标找到一个数。

有什么方法可以结合这两种存储结构的特点呢?

我们可以考虑使用数组存取每一个节点吗?

那肯定是不行的,但我们可以用数组存储这个指针的地址,通过访问数组快速的找到这个节点。

这样就结合了顺序表和链表的特性,减少了算法的时间复杂度。

如下:用数组存储了在链表上的节点,数组的下标就是对应的要找的节点的值。

洛谷:P1160 队列安排(彻底掌握线性表)_第2张图片

 如图,我们可以通过一个结构体数组指针来快速的找到各个节点,在利用链表快速插入、删除。

2.首先我们定义一个结构体,我们可以用一个结构体指针数组存放每个节点的指针,数组的下标就是对应的同学编号,数组的值对应指向该同学编号的指针,这样不仅查找时间为O(1),删除和插入也为O(1)

一、结构体定义:

#include 

using namespace std;

typedef struct Node

{

    int val;

    Node *next;

} Node, *linkedList;

二、初始化结构体指针数组和带头结点链表:然后把第一个同学插入链表,并赋值给结构体指针数组。

洛谷:P1160 队列安排(彻底掌握线性表)_第3张图片

三、写后插的插入函数(p==1):(记得把插入函数的返回值改成指针类型并赋值给结构体指针数组)

        插入函数的传入的第一个参数为当前要插入的编号为k的同学在链表上的位置,x为要插入的同学的编号,返回的是指向要插入节点的指针(不难发现,这个插入只能插在当前节点的后面)所以我们应再写一个插入函数。

洛谷:P1160 队列安排(彻底掌握线性表)_第4张图片

 四.写前插的插入函数(p==0):

        因为我们能通过list1[k]快速找到k节点指针,但却没有k节点前的指针,却可以通过k节点访问x后的节点,所以我们可以考虑先将x节点插入到k节点的后面然后再交换k节点和x节点的值,这样就做了前插。

洛谷:P1160 队列安排(彻底掌握线性表)_第5张图片

五.定义一个ma[100005]数组并初始化值全为0和写一个删除函数。

        数组ma[100005]的用意是记录删除,也就是防止重复删除。

 int ma[100005] = {0}; 

洛谷:P1160 队列安排(彻底掌握线性表)_第6张图片

六、分步骤讲解。

第一步:初始化变量:

 int n, m, ma[100005] = {0}, k, p, x;

    linkedList list = new Node;

    list->next = NULL;

    linkedList *list1;

    list1 = new linkedList[100005];//初始化结构体指针数组

    list1[1] = insert(list, 1);//将第一个同学插入

第二步:写n-1次的插入循环

  cin >> n;
 for (int i = 2; i <= n; i++)
    {
        cin >> k >> p;
        if (p == 1)
        {
            list1[i] = insert(list1[k], i);
        }
        else
        {
            list1[i] = push(list1[k], i);
            list1[k] = list1[i]->next;  //因为采用的是特殊的前插,所以指针也要修改
        }
    }

第三步:写m次的删除循环  (注意避免重复删除)

cin >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> x;
        if (ma[x] == 1)
            continue;   //如果已删除,则跳过本次操作
        ma[x] = 1;
        remove(list1[x]);
    }

第四步:打印结果:

Node *L = list->next;
    while (L)
    {
        cout << L->val << " ";
        L = L->next;
    }

 七、提交结果

洛谷:P1160 队列安排(彻底掌握线性表)_第7张图片

 八、全代码

#include 
using namespace std;
typedef struct Node
{
    int val;
    Node *next;
} Node,*linkedList;
//后插
linkedList insert(Node *list, int x)
{
    Node *p = new Node;
    p->val = x;
    p->next = list->next;
    list->next = p;
    return p;
}
//前插 -->(先后插再换值,相当于后插,多了一步换值)
linkedList push(Node *list, int x)
{
    Node *p = new Node;
    p->val = list->val;
    list->val = x;
    p->next = list->next;
    list->next = p;
    return list;
}
//删除
void remove(Node *list)
{
    Node *p = list->next;
    list->val = p->val;
    list->next = p->next;
    delete p;
}

int main()
{
    int n, m, ma[100005] = {0}, k, p, x;
    linkedList list = new Node;
    list->next = NULL;
    linkedList *list1;
    list1 = new linkedList[100005];
    list1[1] = insert(list, 1);
    cin >> n;
    for (int i = 2; i <= n; i++)
    {
        cin >> k >> p;
        if (p == 1)    //前插
        {
            list1[i] = insert(list1[k], i);
        }
        else        //后插
        {
            list1[i] = push(list1[k], i);
            list1[k] = list1[i]->next;
        }
    }
//删除
    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> x;
        if (ma[x] == 1) //避免重复删除
            continue;
        ma[x] = 1;
        remove(list1[x]);
    }
    //打印结果
    Node *L = list->next;
    while (L)
    {
        cout << L->val << " ";
        L = L->next;
    }
    return 0;
}

如果你的思路和我的一样却有错误,可以考虑是不是在前插操作时忘记该结构体数组指针了;

你可能感兴趣的:(链表,算法)