第三章 链表和list

第三章 链表和list

根据前两部分的学习和总结,我们注意到,链表是通过指针来维护数据元素之间的逻辑关系的,因此在本章利用数组模拟单链表时,数组中的下标仅代表其物理地址,而不代表其逻辑地址,务必进行区分。

这里我们再次对两种链表进行模拟总结

实现方式

首先我们需要明确的是,在算法竞赛中,为了保证运行速度,我们一般利用多个数组配合来模拟链表进行静态实现

定义

  • 数组:单链表和双向链表都需要e[]表示数值域,用于存储结点的值,ne[]表示指针域,用于存储结点的next指针(指向下一个元素的地址),双向链表还需要prev[]表示另一指针域,用于存储结点的prev指针(指向上一个元素的地址)。

    • e[]ne[](包括prev[])的下标(即物理地址)是相同且一一对应的
    • 再次提醒注意:下标代表的是结点的物理地址,而不是逻辑地址。
    • 另外,如果要实现按值查找功能,还需要一个mp[]数组,用于标记各值的位置。
  • 变量:单链表和双向链表都需要h充当头指针,表示头结点的位置,id用于为新结点分配地址。

    • 尽管id在模拟使用过程中是按序递增的,但是其并不是逻辑地址,只是为了方便模拟分配的,请忽略其具体的值,将其视作无序且随机的内容。
const int N = 1e5 + 10;

int h;       /* 头指针 */
int e[N];    /* 数据域 */
int ne[N];   /* 指针域 */
int id;      /* 分配地址给新结点 */
int mp[N];   /* 为了实现按值查找而用作标记各值位置的数组 */

int prev[N]; /* 双向指针还需要的:前驱指针 */

头插

void push_front(int x) {
    id++;
    e[id] = x;
    mp[x] = id;
    ne[id] = ne[h];
    /*双向指针还需要对前驱指针进行修改*/
    prev[id] = h;
    prev[ne[h]] = id;
    /*在此我们规定,无论是单双指针还是任何操作,原next指针必须最后进行修改,避免数据丢失等错误*/
    ne[h] = id;
}

时间复杂度:O(1)

遍历链表

单向指针和双向指针的遍历方式相同,实现如下:

/*打印链表*/
void print() {
    for (int i = ne[h]; i ; i = ne[i]) {
        cout << e[i] << " ";
    }
}

时间复杂度:O(n)

按值查找

单向双向链表相同,直接利用标记数组即可。

/*按值查找*/
int find(int x) {
    return mp[x];
}

时间复杂度:O(1)

在任意位置之后插入

/*在第p之后插入一个值x*/
void insert_back(int p, int x) {
    id++;
    e[id] = x;
    mp[x] = id;
    ne[id] = ne[p];
    /*双向指针还需要对前驱指针进行修改*/
    prev[id] = p;
    prev[ne[p]] = id;
    /*同样,将原next的指针修改放在最后*/
    ne[p] = id;
}

时间复杂度:O(1)

在任意位置之前插入

针对于双向链表,我们还可以实现在任意位置之前插入。

/*在第p之前插入一个值x*/
void insert_front(int p, int x) {
    id++;
    e[id] = x;
    mp[x] = id;
    ne[id] = p;
    prev[id] = prev[p]; 
    /*同样,将原next的指针修改放在最后,这里还涉及到prev指针来找到原next指针,所以需要将prev放在最后*/
    ne[prev[p]] = id;
    prev[p] = id;
}

时间复杂度:O(1)

删除任意位置之后的元素

单链表比较便于实现删除任意元素之后的元素。

void del(int p) {
    if(ne[p]{
        mp[e[ne[p]]] = 0;
        ne[p] = ne[ne[p]];
    })
}

时间复杂度:O(1)

删除任意位置的元素

双向链表可以实现删除任意位置的元素。

void del(int p) {
    if(ne[p]) {
        mp[e[p]] = 0;
        ne[prev[p]] = ne[p];
        prev[ne[p]] = prev[p];
    }
}

时间复杂度:O(1)

正如本章开头所说,链表是通过指针来维护数据元素之间的逻辑关系的,因此在本章利用数组模拟单链表时,数组中的下标仅代表其物理地址,而不代表其逻辑地址。

请忽略掉相关数组和标记指针之间的逻辑位置,重在分析利用数据域和指针域来模拟链表的过程,当不便理解时,可以更换相关数组下标的表示方式,画图进行分析。

你可能感兴趣的:(数据结构及STL,链表,list,数据结构)