【数据结构与算法】【知识体系整理】2-链表
1 链表
为了更好地理解链表,我们和顺序表对比。顺序表需要一段连续的存储空间,且初始化时就固定了长度。如果后续发现空间不够用,则需要扩容。
链表的每个节点都是“数据+指针”的格式,通过指针将各元素连接起来,因此不需要连续的存储空间。链表的长度不固定,也不需要扩容。
头指针指向链表中第一个节点的地址。最后一个节点的指针为NULL。
链表的结构定义:data、next指针。
单向链表的操作
插入
如想将节点new插入到链表中第n个位置,首先从链表头部向后遍历,找到第n-1个节点。使节点new指向第n个节点,然后再使得节点n-1指向节点new即可。
删除
如想删除节点n,首先从链表头部向后遍历,找到第n-1个节点。为了避免内存泄露,要先定义一个临时指针,指向待删除节点。然后让节点n-1的后继指针指向节点n+1。
单向循环链表
单向循环链表的尾节点的后继指针指向头节点,形成一个环路。head指向整个单向循环链表的尾节点(考虑到插入到index为0的位置)。在插入、删除等操作中,要注意保持head指向单向循环列表的尾结点。
leetcode练习:19、24、83、141、160、202、203、206、234、237。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
typedef struct List {
Node head;
int size;
} List;
Node *node_create(int val)
{
Node *node = (Node *)malloc(sizeof(Node));
if (node == NULL) return NULL;
node->data = val;
node->next = NULL;
return node;
}
void node_destroy(Node *node)
{
if (node == NULL) return;
free(node);
return;
}
List *list_create()
{
List *list = (List *)malloc(sizeof(List));
if (list == NULL) return NULL;
list->head.next = NULL;
list->size = 0;
return list;
}
void list_destroy(List *list)
{
if (list == NULL) return;
Node *p = list->head.next;
while (p != NULL) {
list->head.next = p->next;
free(p);
p = list->head.next;
}
free(list);
return;
}
int list_insert(List *list, int idx, int val)
{
if (list == NULL) return -1;
if (idx < 0 || idx > list->size) return -1;
Node *new_node = node_create(val);
if (new_node == NULL) return -1;
Node *p = &(list->head);
while (idx--) {
p = p->next;
}
new_node->next = p->next;
p->next = new_node;
list->size++;
return 0;
}
int list_erase(List *list, int idx)
{
if (list == NULL) return -1;
if (idx < 0 || idx > list->size - 1) return -1;
Node *p = &(list->head);
Node *q = NULL;
while (idx--) {
p = p->next;
}
q = p->next;
p->next = q->next;
node_destroy(q);
list->size--;
return 0;
}
void list_reverse(List *list)
{
if (list == NULL) return;
Node *p = list->head.next;
Node *q = NULL;
list->head.next = NULL;
while (p != NULL) {
q = p->next;
p->next = list->head.next;
list->head.next = p;
p = q;
}
return;
}
void list_output(List *list)
{
if (list == NULL) return;
Node *p = list->head.next;
printf("List: HEAD->");
while (p != NULL) {
printf("%d->", p->data);
p = p->next;
}
printf("NULL\n");
return;
}
int main()
{
srand(time(0));
#define MAX_OP 20
List *l = list_create();
for (int i = 0; i < MAX_OP; i++)
{
int op = rand() % 4;
int val = rand() % 100;
int ind = rand() % (l->size + 3) -1;
switch (op)
{
case 0:
case 1:
printf("Insert %d at %d to list = %d\n", val, ind, list_insert(l, ind, val));
break;
case 2:
printf("Reverse the List!\n");
list_reverse(l);
break;
case 3:
printf("Erase the item at %d from list = %d\n", ind, list_erase(l, ind));
break;
default:
break;
}
list_output(l);
printf("\n");
}
#undef MAX_OP
list_destroy(l);
return 0;
}
单项循环链表、双向链表