引入
链表是一种用于存储数据的数据结构,通过如链条一般的指针来连接元素。它的特点是插入与删除数据十分方便,但寻找与读取数据的表现欠佳。
与数组的区别
链表和数组都可用于存储数据。与链表不同,数组将所有元素按次序依次存储。不同的存储结构令它们有了不同的优势:
链表因其链状的结构,能方便地删除、插入数据,操作次数是 O(1)。但也因为这样,寻找、读取数据的效率不如数组高,在随机访问数据中的操作次数是 O(N)。
数组可以方便地寻找并读取数据,在随机访问中操作次数是 0(1)。但删除、插入的操作次数是O(N)次。
单向链表
单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。
//实现
struct Node {
int value;
Node *next;
};
双向链表
双向链表中同样有数据域和指针域。不同之处在于,指针域有左右(或上一个、下一个)之分,用来连接上一个结点、当前结点、下一个结点。
//实现
struct Node {
int value;
Node *left;
Node *right;
};
向链表中插入(写入)数据
单向链表
//实现
void insertNode(int i, Node *p) {
Node *node = new Node;
node->value = i;
node->next = p->next;
p->next = node;
}
单向循环链表
将链表的头尾连接起来,链表就变成了循环链表。由于链表首尾相连,在插入数据时需要判断原链表是否为空:为空则自身循环,不为空则正常插入数据。
大致流程如下:
初始化待插入的数据 node;
判断给定链表 p 是否为空;
若为空,则将 node 的 next 指针和 p 都指向自己;
否则,将 node 的 next 指针指向 p 的下一个结点;
将 p 的 next 指针指向 node。
//实现
void insertNode(int i, Node *p) {
Node *node = new Node;
node->value = i;
node->next = NULL;
if (p == NULL) {
p = node;
node->next = node;
} else {
node->next = p->next;
p->next = node;
}
}
双向循环链表
在向双向循环链表插入数据时,除了要判断给定链表是否为空外,还要同时修改左、右两个指针。
大致流程如下:
初始化待插入的数据 node;
判断给定链表 p 是否为空;
若为空,则将 node 的 left 和 right 指针,以及 p 都指向自己;
否则,将 node 的 left 指针指向 p;
将 node 的 right 指针指向 p 的右结点;
将 p 右结点的 left 指针指向 node;
将 p 的 right 指针指向 node。
//实现
void insertNode(int i, Node *p) {
Node *node = new Node;
node->value = i;
if (p == NULL) {
p = node;
node->left = node;
node->right = node;
} else {
node->left = p;
node->right = p->right;
p->right->left = node;
p->right = node;
}
}