一、 基本特征:
内存中不连续的节点序列, 节点之间通过next指针彼此相连; 每个节点的next指针都指向下一个节点,最后一个节点的next指针为NULL.
二、基本操作:
插入、删除、遍历
三、形式:
单向链表, 双向链表, 单向循环链表, 双向循环链表
四、实现要点:
追加: 将新分配节点的地址赋给原链表尾端节点的next指针
插入: 将前节点中存储的后节点地址赋给新节点的next指针,将新节点的地址赋给前节点的next指针
删除: 将前节点的next指针赋值为待删除节点的next指针, 对于单向链表而言, 寻找前节点会有一定开销
遍历: 沿着next指针依次访问链表中的各个节点
伪随机访问: 遍历+计数
范例:
A、双向链表
// .h
// 双向线性链表
#ifndef _L_H
#define _L_H
#include <sys/types.h>
// 节点
typedef struct ListNode {
int data; // 数据
struct ListNode* prev; // 前指针
struct ListNode* next; // 后指针
} LIST_NODE;
typedef struct List {
LIST_NODE* head; // 头指针
LIST_NODE* tail; // 尾指针
LIST_NODE* frwd; // 正向迭代指针
LIST_NODE* bkwd; // 反向迭代指针
} LIST;
// 初始化为空链表
void list_init(LIST* list);
// 释放剩余节点并恢复到初始化状态
void list_deinit(LIST* list);
// 判断是否为空
int list_empty(LIST* list);
// 追加
void list_append(LIST* list, int data);
// 前插
void list_insert(LIST* list, size_t pos, int data);
// 随机访问
int* list_at(LIST* list, size_t pos);
// 删除
void list_erase(LIST* list, size_t pos);
// 删除所有匹配数据
void list_remove(LIST* list, int data);
// 清空
void list_clear(LIST* list);
// 长度
size_t list_size(LIST* list);
// 开始正向迭代
void list_begin(LIST* list);
// 继续正向迭代
int* list_next(LIST* list);
// 获取正向迭代
int* list_current(LIST* list);
// 终止正向迭代
void list_end(LIST* list);
// 判断正向迭代是否终止
int list_done(LIST* list);
// 开始反向迭代
void list_rbegin(LIST* list);
// 继续反向迭代
int* list_rnext(LIST* list);
// 获取反向迭代
int* list_rcurrent(LIST* list);
// 终止反向迭代
void list_rend(LIST* list);
// 判断反向迭代是否终止
int list_rdone(LIST* list);
#endif
// .c
#include <stdio.h>
#include <stdlib.h>
#include "clink.h"
// 创建节点
static LIST_NODE* create_node(int data, LIST_NODE* prev, LIST_NODE* next)
{
LIST_NODE* node = (LIST_NODE*)malloc(sizeof(LIST_NODE));
node->data = data;
node->prev = prev;
node->next = next;
return node;
}
// 销毁节点
static LIST_NODE* destroy_node(LIST_NODE* node, LIST_NODE** prev)
{
LIST_NODE* next = node->next;
if (prev) {
*prev = node->prev;
}
free(node);
return next;
}
// 初始化为空链表
void list_init(LIST* list)
{
list->head = NULL;
list->tail = NULL;
}
// 释放剩余节点并恢复到初始化状态
void list_deinit(LIST* list)
{
while (list->head) {
list->head = destroy_node(list->head, NULL);
}
list->tail = NULL;
}
// 判断是否为空
int list_empty(LIST* list)
{
return ! list->head && ! list->tail;
}
// 追加
void list_append(LIST* list, int data)
{
list->tail = create_node(data, list->tail, NULL);
if (list->tail->prev) {
list->tail->prev->next = list->tail;
}
else {
list->head = list->tail;
}
}
// 前插
void list_insert(LIST* list, size_t pos, int data)
{
LIST_NODE* find = NULL;
for (find = list->head; find; find = find->next) {
if (!pos--) {
LIST_NODE* node = create_node(data, find->prev, find);
if (node->prev) {
node->prev->next = node;
}
else {
list->head = node;
}
node->next->prev = node;
break;
}
}
}
// 随机访问
int* list_at(LIST* list, size_t pos)
{
LIST_NODE* find = NULL;
for (find = list->head; find; find = find->next)
{
if (!pos--) {
break;
}
}
return &find->data;
}
// 删除
void list_erase(LIST* list, size_t pos)
{
LIST_NODE* find = NULL;
for (find = list->head; find; find = find->next) {
if (!pos--) {
LIST_NODE* prev = NULL;
LIST_NODE* next = destroy_node(find, &prev);
if (prev) {
prev->next = next;
}
else {
list->head = next;
}
if (next) {
next->prev = prev;
}
else {
list->tail = prev;
}
return;
}
}
}
// 删除所有匹配数据
void list_remove(LIST* list, int data)
{
LIST_NODE *find = NULL, *next = NULL;
for (find = list->head; find; find = next) {
next = find->next;
if (find->data == data) {
LIST_NODE* prev = NULL;
next = destroy_node(find, &prev);
if (prev) {
prev->next = next;
}
else {
list->head = next;
}
if (next) {
next->prev = prev;
}
else {
list->tail = prev;
}
}
}
}
// 清空
void list_clear(LIST* list)
{
list_deinit(list);
}
// 长度
size_t list_size(LIST* list)
{
size_t count = 0;
LIST_NODE* find;
for (find = list->head; find; find = find->next) {
++count;
}
return count;
}
// 开始正向迭代
void list_begin(LIST* list)
{
list->frwd = list->head;
}
// 继续正向迭代
int* list_next(LIST* list)
{
int* data = &list->frwd->data;
list->frwd = list->frwd->next;
return data;
}
// 获取正向迭代
int* list_current(LIST* list)
{
return &list->frwd->data;
}
// 终止正向迭代
void list_end(LIST* list)
{
list->frwd = list->tail->next;
}
// 判断正向迭代是否终止
int list_done(LIST* list)
{
return list->frwd == list->tail->next;
}
// 开始反向迭代
void list_rbegin(LIST* list)
{
list->bkwd = list->tail;
}
// 继续反向迭代
int* list_rnext(LIST* list)
{
int * data = &list->bkwd->data;
list->bkwd = list->bkwd->prev;
return data;
}
// 获取反向迭代
int* list_rcurrent(LIST* list)
{
return &list->bkwd->data;
}
// 终止反向迭代
void list_rend(LIST* list)
{
list->bkwd = list->head->prev;
}
// 判断反向迭代是否终止
int list_rdone(LIST* list)
{
return list->bkwd == list->head->prev;
}
#include <stdio.h>
#include <stdlib.h>
#include "clink.h"
int main(void)
{
LIST list;
list_init(&list);
list_append(&list, 10);
list_append(&list, 30);
list_append(&list, 50);
list_insert(&list, 1, 20);
list_insert(&list, 3, 40);
for (list_begin(&list); !list_done(&list); list_next(&list)) {
printf("%d ", ++*list_current(&list));
}
printf("\n");
list_append(&list, 41);
list_append(&list, 41);
list_begin(&list);
while (!list_done(&list)) {
printf("%d ", *list_next(&list));
}
printf("\n");
list_erase(&list, 1);
list_remove(&list, 41);
list_rbegin(&list);
while (!list_rdone(&list)) {
printf("%d ", *list_rnext(&list));
}
printf("\n");
size_t pos, size = list_size(&list);
for (pos = 0; pos < size; ++pos) {
printf("%d ", --*list_at(&list, pos));
}
printf("\n");
list_clear(&list);
printf("%d, %zu\n", list_empty(&list), list_size(&list));
list_deinit(&list);
return 0;
}
/*
Output:
11 21 31 41 51
11 21 31 41 51 41 41
51 31 11
10 30 50
1, 0
*/
B、单向链表练习
(1)实现list_append()、list_size()、list_print()和list_rprint(), 分别用于单向链表的追加、测长、正向打印和反向打印
(2)实现list_reverse()函数, 用于将单向链表逆转
(3)实现list_middle()函数, 用于获取单向链表的中间值, 平均时间复杂度不得超过O(N)级
// .h
// 双向线性链表
#ifndef _L_H
#define _L_H
#include <sys/types.h>
// (1)实现list_append()、list_size()、list_print()和list_rprint(), 分别用于单向链表的追加、测长、正向打印和反向打印
// (2)实现list_reverse()函数, 用于将单向链表逆转
// (3)实现list_middle()函数, 用于获取单向链表的中间值, 平均时间复杂度不得超过O(N)级
// 节点
typedef struct ListNode {
int data; // 数据
struct ListNode* next; // 前指针
} LIST_NODE;
typedef struct List {
LIST_NODE* head; // 头指针
LIST_NODE* tail; // 尾指针
size_t size;
} LIST;
// 初始化为空链表
void list_init(LIST* list);
// 释放剩余节点并恢复到初始化状态
void list_deinit(LIST* list);
// 判断是否为空
int list_empty(LIST* list);
// 追加
void list_append(LIST* list, int data);
// 获取节点的前指针
LIST_NODE* list_prev(LIST* list, LIST_NODE* node);
// 长度
size_t list_size(LIST* list);
// 正向打印
void list_print(LIST* list);
// 反向打印
void list_rprint(LIST* list);
void list_rprint2(LIST* list);
// 单向链表逆转
LIST list_reverse(LIST* list);
void list_reverse2(LIST* list);
// 获取单向链表中间值
void list_middle(LIST* list);
void list_middle2(LIST* list);
#endif
// .c
#include <stdio.h>
#include <stdlib.h>
#include "clink.h"
// 创建节点
static LIST_NODE* create_node(int data)
{
LIST_NODE* node = (LIST_NODE*)malloc(sizeof(LIST_NODE));
node->data = data;
node->next = NULL;
return node;
}
// 销毁节点
static LIST_NODE* destroy_node(LIST_NODE* node)
{
LIST_NODE* next = node->next;
free(node);
return next;
}
// 初始化为空链表
void list_init(LIST* list)
{
list->head = NULL;
list->tail = NULL;
list->size = 0;
}
// 释放剩余节点并恢复到初始化状态
void list_deinit(LIST* list)
{
while (list->head) {
list->head = destroy_node(list->head);
}
list->tail = NULL;
list->size = 0;
}
// 判断是否为空
int list_empty(LIST* list)
{
return ! list->size;
}
// 追加
void list_append(LIST* list, int data)
{
LIST_NODE* node = create_node(data);
if (list->tail) {
list->tail->next = node;
}
else {
list->head = node;
}
list->tail = node;
++list->size;
}
// 获取节点的前指针
LIST_NODE* list_prev(LIST* list, LIST_NODE* node)
{
LIST_NODE* find = NULL;
for (find = list->head; find; find = find->next) {
if (find->next == node) {
break;
}
}
return find;
}
// 长度
size_t list_size(LIST* list)
{
return list->size;
}
void list_print(LIST* list)
{
LIST_NODE* find = NULL;
for (find = list->head; find; find = find->next) {
printf("%d ", find->data);
}
printf("\n");
}
void list_rprint(LIST* list)
{
LIST_NODE* find = NULL;
for (find = list->tail; find; find = list_prev(list, find)) {
printf("%d ", find->data);
}
printf("\n");
}
// 反向打印以参数head的目标节点为首的子链表
// 当你下一步要做的事,恰好是你正在做的事, 就可以使用递归
static void rprint(LIST_NODE* head)
{
// 此处利用了函数栈的原理, 找到链表最后一项非空值, 然后从函数参数栈pop, 逆向打印数据
// 函数参数栈事从右向左压栈的
if (head) {
rprint(head->next);
printf("%d ", head->data);
}
}
// 第二种方式逆向打印
void list_rprint2(LIST* list)
{
rprint(list->head);
printf("\n");
}
// 第三种方式逆向打印: 先正向打印, 然后把结构存到栈里, 再pop出来
// 单向链表逆转
LIST list_reverse(LIST* list)
{
LIST newlist;
list_init(&newlist);
LIST_NODE* find = NULL;
for (find = list->tail; find; find = list_prev(list, find)) {
list_append(&newlist, find->data);
}
list_deinit(list);
return newlist;
}
// 逆转以参数node的目标节点为首的子链表
static void reverse(LIST_NODE* node)
{
if (node && node->next) {
reverse(node->next);
node->next->next = node;
node->next = NULL;
}
}
void list_reverse2(LIST* list)
{
reverse(list->head);
LIST_NODE* swap = list->head;
list->head = list->tail;
list->tail = swap;
}
// 获取单向链表中间值
void list_middle(LIST* list)
{
int leng = list_size(list);
int middle;
if (leng % 2) {
middle = leng / 2;
}
else {
middle = leng / 2 - 1;
}
LIST_NODE* find = NULL;
for (find = list->head; find; find = find->next) {
if (!middle--) {
printf("中间值 = %d \n", find->data);
}
}
}
// 定义两个指针, 一个每次走两格, 一个每次走一格, 快指针到头的时候, 就是慢指针到中间的时候
void list_middle2(LIST* list)
{
LIST_NODE *fast = NULL, *slow = NULL;
for (fast = slow = list->head; fast->next && fast->next->next; fast = fast->next->next) {
slow = slow->next;
}
printf("第二种方式获取 中间值 = %d \n", slow->data);
}
#include <stdio.h>
#include <stdlib.h>
#include "clink.h"
void reverseTest(void);
int main(void)
{
LIST list;
list_init(&list);
list_append(&list, 10);
list_append(&list, 30);
list_append(&list, 50);
list_print(&list);
list_append(&list, 41);
list_append(&list, 41);
printf("size = %zu\n", list_size(&list));
list_rprint(&list);
list_rprint2(&list);
list = list_reverse(&list);
list_print(&list);
list_deinit(&list);
reverseTest();
return 0;
}
void reverseTest(void)
{
LIST list;
list_init(&list);
int i;
for (i = 0; i < 11; ++i) {
list_append(&list, i);
}
list_print(&list);
list_reverse2(&list);
list_print(&list);
list_middle(&list);
list_middle2(&list);
list_deinit(&list);
}
/*
Output:
10 30 50
size = 5
41 41 50 30 10
41 41 50 30 10
41 41 50 30 10
0 1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1 0
中间值 = 5
第二种方式获取 中间值 = 5
*/