Bootstrap

算法比赛必备-数组模拟链表

为什么要用数组模拟

众所周知,链表是通过指针将所有的结点相连接,以此来达到快速插入和删除的数据结构。但是在

很多时候,特别是在学校、书本里面,大多都是用的结构体,而使用结构体的劣势也显而易见:前戏长,运算慢。在我们平时做算法题还是练习的时候,我们推荐使用数组模拟。

当然,队列、栈、二叉树 这些也可以用数组模拟。

结构体实现链表

// 定义结构体
typedef struct LNode  {
    ElemType  data;
    struct LNode *next;
}LNode, *LinkList;


// 在单链表中取得第i个元素
Status getElem L(LinkList L,int i,ElemType &e) {
    // L为带头结点的单链表指针。
    // 当第i个元素存在时,其赋给e并返回,否则返回Error。
    p = L->next, j=1; // 初始化,p指向第一个结点,j为计数器。
    while(p && j<i) { // 从头结点开始查找,知道p指向第i个结点或 p 为空
        p = p->next ;
        ++j;
    }
    if(!p ||j>i) return ERROR;  // 第i个元素不存在
    e = p-> data; // 取第i个元素并返回
    return e;
}
// 在单链表中第i个位置之前插入元素e
Status ListInsert L(LinkList &L,int i, ElemType e) { // & 返回哪个参数
    p = L , j=0;
    while(p && j < i-1) { // 寻找第i-1个结点
        p = p-> next;
        ++j;
    }
    if(!p || j>i-1) return ERROR; // i小于1 或者 i大于表长+1(健壮性处理)
    s = (LinkList)malloc (sizeof LNode);
    s->data = e;
    s->next = p->next;
    p -> next = s;
    // 指针操作,注意前后顺序
}
// 在单链表中删除第i个元素 并返回删除的值为e
Status ListDelete L(LinkList &L,int i,ElemType &e) {
    p = L,j=0; // 初始化
    while(p->next && j<i-1){
        p = p->next ;
        ++j;
    }
    if(!(p->next) || j>i-1)  return ERROR;
    q = p->next ;
    p->next = q->next; // 删除并释放结点(q)
    e = q->data;
    free(q);
    // 注意操作顺序
}







数组实现单链表

定义变量: idx,head , h[ ], ne[ ] , e[ ] 。

idx:链表的下标。 在进行删除,插入操作时作为辅助元素帮助操作。

head:链表头结点。

ne[]:指向当前元素位置的下一个位置。

e[]:保存当前节点的值。

定义基本方法:init_list() 、 insert_head() 、add() 、 remove() 。


// 对链表进行初始化。
void init_list(){
    idx = 0; // 令当前下标0 (c++全局变量初试为0)
    head = -1; // 链表的头节点要指向-1
}

//将x插入到头节点上
void insert_head(int x) {
    e[idx] = x;  //第一步,先将值放进去
    ne[idx] = head;  //head作为一个指针指向上一个头结点,现在ne[idx] = head 相当于 新添加的元素
                     // 指向上一个更新的头结点。
    head = idx;  //更新head结点进行迭代。
    idx ++;      //指针向下移一位。
}

//将x插入到下标为k的点的后面
void add(int k, int x){
    e[idx] = x;//先将元素插进去
    ne[idx] = ne[k];//让元素x配套的指针,指向它要占位的元素的下一个位置
    ne[k] = idx;//让原来元素的指针指向自己
    idx ++;//将idx向后挪
}

//将下标是k的结点后面的点删除
void remove(int k){
    ne[k] = ne[ne[k]];  //让k的指针指向k下一个结点的下一个结点 。
}

 值得注意的是 ,在数组模拟链表这几个基本方法中,需尤为注意操作的顺序。例如:在 add() 方法中,如果把

ne[idx] = ne[k] 和  ne[k] = idx  互换位置 ,那结果就不一样了, 因为在其之前, ne[k] 的位置已经不一样了 ,这个时候再去指向 ne[k] 就错了。

例题示范

 借鉴 Acwing 里面一道模板题 

826. 单链表 - AcWing题库

 思路:定义好基本变量后 ,使用数组模拟链表或者使用c++自带的STL(之前在洛谷看到过一句话,很深刻:我们通过努力去尝试AC的每一道题,不是目标,而是训练。所以说学习这种东西真的是多多益善 0.0) 。然后写好各自方法体。

AC代码: 

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int head, e[N],h[N],ne[N], idx;
int n;
void init(){
    head = -1;
    idx = 0;
}
// 头结点插值
void inset_head(int x){
    e[idx] = x;
    ne[idx] = head; 
    head = idx;
    idx ++ ;
}
// 将x插入到下标是k的结点的后面
void add(int k, int x){
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx ++ ;
}
// 将k点后面的点删除
void remove(int k){
    ne[k] = ne[ne[k]];
}
int main(){
    cin >> n;
    init();
    while (n--){
        int k, x;
        char op;

        cin >> op;
        if (op == 'H'){
            cin >> x;
            inset_head(x);
        }
        else if (op == 'D'){
            cin >> k;
            if (!k) head = ne[head];
            remove(k - 1);
        }
        else{
            cin >> k >> x;
            add(k - 1, x);
        }
    }
    for (int i = head; i != -1; i = ne[i]) cout << e[i] <<" ";
    return 0;
}

;