链表

简介 #

  • 链表只能按顺序依次访问元素,而数组支持随机访问.
  • 链表支持在任意位置插入或删除元素,而数组不支持.

链表节点 #

用一个结构体表示链表的节点,其中可以存储任意数据.每个节点有 prev 和 next 两个指针,指向前后相邻的节点.

struct Node {
    int val; // 数据(可以是任意类型)
    Node *prev, *next; // 指针
};

初始化 #

初始化链表时,额外建立两个节点 head 和 tail 代表链表头尾,把实际节点存储在 head 与 tail 之间,简化链表边界的判断.

Node *head, *tail;

void init() {
    head = new Node(); tail = new Node();
    head->next = tail; tail->prev = next;
}

插入/删除节点 #

如何在 1 和 2 之间插入 3 ?

删除节点运用到类似的方法.

void insert(Node *before, int val) { // 在节点 before 后面插入数据为 val 的新节点
    q = new Node(); q->val = val;
    Node *after = before->next;
    after->prev = q, q->next = after;   // Step 1
    before->next = q, q->prev = before; // Step 2
}

void remote(Node *p) { // 删除节点 p
    p->prev->next = p->next;
    p->next->prev = p->prev;
    delete p;
}

查找节点 #

Node* indexOf(int val) { // 返回链表中第一个出现的值为 val 的节点指针
    for (Node *p = head->next; p != tail; p = p->next)
        if (p->val == val) return p;
    return NULL;
}

清空链表 #

void clear() {
    while(head != tail) {
        head = head->next;
        delete head->prev;
    }
    delete tail;
}

用数组模拟链表 #

使用指针动态分配空间,效率较低且不稳定.一般使用数组模拟链表.

struct Node {
    int val;
    int prev, next;
} node[];

int head, tail, tot;

void init() {
    head = 0, tail = 1, tot = 2;
    node[head].next = tail;
    node[tail].prev = head;
}

void insert(int p, int val) {
    int q = ++ tot; node[q].val = val;
    node[node[p].next].prev = q; node[q].next = node[p].next;
    node[p].next = q, node[q].next = p;
}

void remove(int p) {
    node[node[p].prev].next = node[p].next;
    node[node[p].next].prev = node[p].prev;
}

int indexOf(int val) {
    for (int p = node[head].next; p != tail; p = node[p].next)
        if (node[p].val == val) return p;
    return -1;
}

void clear() {
    head = tail = tot = 0;
    memset(node, 0, sizeof node);
}