嵌入式学习 双向链表 循坏链表 内核链表
1.双向链表
双向链表的基本概念
双向链表是一种常见的数据结构,每个节点包含两个指针,分别指向前驱节点和后继节点。与单向链表相比,双向链表支持双向遍历,但需要更多的内存空间来存储额外的指针。
双向链表的节点定义
在C语言中,双向链表的节点通常通过结构体定义,包含数据域和两个指针域:
typedef struct Node {int data; // 数据域struct Node* prev; // 指向前驱节点的指针struct Node* next; // 指向后继节点的指针
} Node;
创建双向链表
动态分配内存创建新节点,并初始化指针:
Node* createNode(int data) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->prev = NULL;newNode->next = NULL;return newNode;
}
插入节点
在头部插入节点
void insertAtHead(Node** head, int data) {Node* newNode = createNode(data);if (*head == NULL) {*head = newNode;return;}newNode->next = *head;(*head)->prev = newNode;*head = newNode;
}
在尾部插入节点
void insertAtTail(Node** head, int data) {Node* newNode = createNode(data);if (*head == NULL) {*head = newNode;return;}Node* temp = *head;while (temp->next != NULL) {temp = temp->next;}temp->next = newNode;newNode->prev = temp;
}
在指定位置插入节点
void insertAtPosition(Node** head, int data, int pos) {if (pos == 0) {insertAtHead(head, data);return;}Node* newNode = createNode(data);Node* temp = *head;for (int i = 0; i < pos - 1 && temp != NULL; i++) {temp = temp->next;}if (temp == NULL) {printf("Position out of range\n");return;}newNode->next = temp->next;if (temp->next != NULL) {temp->next->prev = newNode;}temp->next = newNode;newNode->prev = temp;
}
删除节点
删除头节点
void deleteAtHead(Node** head) {if (*head == NULL) {printf("List is empty\n");return;}Node* temp = *head;*head = (*head)->next;if (*head != NULL) {(*head)->prev = NULL;}free(temp);
}
删除尾节点
void deleteAtTail(Node** head) {if (*head == NULL) {printf("List is empty\n");return;}Node* temp = *head;while (temp->next != NULL) {temp = temp->next;}if (temp->prev != NULL) {temp->prev->next = NULL;} else {*head = NULL;}free(temp);
}
删除指定位置的节点
void deleteAtPosition(Node** head, int pos) {if (*head == NULL) {printf("List is empty\n");return;}Node* temp = *head;if (pos == 0) {deleteAtHead(head);return;}for (int i = 0; i < pos && temp != NULL; i++) {temp = temp->next;}if (temp == NULL) {printf("Position out of range\n");return;}if (temp->next != NULL) {temp->next->prev = temp->prev;}if (temp->prev != NULL) {temp->prev->next = temp->next;}free(temp);
}
遍历双向链表
正向遍历
void traverseForward(Node* head) {Node* temp = head;while (temp != NULL) {printf("%d ", temp->data);temp = temp->next;}printf("\n");
}
反向遍历
void traverseBackward(Node* head) {if (head == NULL) {printf("List is empty\n");return;}Node* temp = head;while (temp->next != NULL) {temp = temp->next;}while (temp != NULL) {printf("%d ", temp->data);temp = temp->prev;}printf("\n");
}
双向链表的应用场景
双向链表适用于需要频繁双向遍历的场景,如浏览器历史记录管理、撤销操作实现等。其优势在于可以高效地访问前驱和后继节点,但缺点是占用更多内存。
2.循环链表
循环链表的概念
循环链表是一种链式存储结构,其特点是表中最后一个节点的指针域指向头节点(或第一个节点),形成一个环。与普通单链表相比,循环链表可以从任意节点出发遍历整个链表。
循环链表的实现
以下以C语言实现单向循环链表为例,包含基本操作:
定义节点结构
typedef struct Node {int data;struct Node *next;
} Node;
创建循环链表
初始化一个空链表时,头节点的 next
指向自身:
Node* createCircularList() {Node *head = (Node*)malloc(sizeof(Node));if (head != NULL) {head->next = head; // 自成环}return head;
}
插入节点
在链表尾部插入新节点:
void insertNode(Node *head, int value) {Node *newNode = (Node*)malloc(sizeof(Node));newNode->data = value;newNode->next = head; // 新节点指向头节点Node *current = head;while (current->next != head) {current = current->next;}current->next = newNode; // 原尾节点指向新节点
}
删除节点
删除指定值的节点:
void deleteNode(Node *head, int value) {Node *current = head->next;Node *prev = head;while (current != head) {if (current->data == value) {prev->next = current->next;free(current);break;}prev = current;current = current->next;}
}
遍历链表
打印所有节点数据:
void traverseList(Node *head) {Node *current = head->next;while (current != head) {printf("%d ", current->data);current = current->next;}printf("\n");
}
双向循环链表
双向循环链表的每个节点包含前驱(prev
)和后继(next
)指针,尾节点的 next
指向头节点,头节点的 prev
指向尾节点。
typedef struct DNode {int data;struct DNode *prev;struct DNode *next;
} DNode;
应用场景
- 轮询调度:操作系统中的进程调度。
- 游戏开发:循环遍历角色或道具列表。
- 约瑟夫问题:经典循环链表求解问题。
注意事项
- 避免无限循环:遍历时需设置终止条件(如回到头节点)。
- 内存管理:动态分配节点后需手动释放内存。
- 头节点处理:插入/删除操作需维护循环性质。
通过合理设计,循环链表可高效处理环形数据逻辑问题。
3.内核链表
1.概念:
- Linux内核中所使用到的一种链表结构
- 使用同一种结构可以存储不同类型的链表
- 普通链表:链表节点中包含数据
- 内核链表:数据中包含链表节点
2.结构描述:
- 创建:创建空白节点即可
- 插入:申请节点,并按照之前学习的循环链表插入即可(插入的链表节点首地址即数据节点首地 址)
- 访问节点:通过遍历找到每个节点的首地址,强制类型转换即可获得对应数据空间首地址
3.使用内核链表
1. 可以参考list.h中关于内核链表的常见操作:
/**/Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>This file is part of GlusterFS.This file is licensed to you under your choice of the GNU LesserGeneral Public License, version 3 or any later version (LGPLv3 orlater), or the GNU General Public License, version 2 (GPLv2), in allcases as published by the Free Software Foundation.#ifndef _LLIST_H#define _LLIST_H/* 内核链表中的节点类型 */struct list_head {struct list_head *next;struct list_head *prev;};/* 初始化空白头结点 */#define INIT_LIST_HEAD(head) do {
\(head)->next = (head)->prev = head; \} while (0)/* 头插法 */static inline voidlist_add (struct list_head *new, struct list_head *head){new->prev = head;new->next = head->next;new->prev->next = new;new->next->prev = new;
}/* 尾插法 */static inline voidlist_add_tail (struct list_head *new, struct list_head *head){new->next = head;new->prev = head->prev;new->prev->next = new;new->next->prev = new;}/* 按指定顺序插入 *//* This function will insert the element to the list in a order.Order will be based on the compare function provided as a input.If element to be inserted in ascending order compare should return:0: if both the arguments are equal>0: if first argument is greater than second argument<0: if first argument is less than second argument */static inline voidlist_add_order (struct list_head *new, struct list_head *head,int (*compare)(struct list_head *, struct list_head *)){struct list_head *pos = head->prev;while ( pos != head ) {if (compare(new, pos) >= 0)break;/* Iterate the list in the reverse order. This will
havebetter efficiency if the elements are inserted in
theascending order */pos = pos->prev;}list_add (new, pos);}/* 将节点移出所属的链表 */static inline voidlist_del (struct list_head *old){old->prev->next = old->next;old->next->prev = old->prev;old->next = (void *)0xbabebabe;old->prev = (void *)0xcafecafe;}/* 将节点移出所属的链表,并初始化 */static inline voidlist_del_init (struct list_head *old){old->prev->next = old->next;old->next->prev = old->prev;old->next = old;old->prev = old;}/* 将节点移动到另一个链表的头部 */static inline voidlist_move (struct list_head *list, struct list_head *head){list_del (list);list_add (list, head);}/* 将节点移动到另一个链表的尾部 */static inline voidlist_move_tail (struct list_head *list, struct list_head *head){list_del (list);list_add_tail (list, head);}/* 判断链表是否为空 */static inline intlist_empty (struct list_head *head){return (head->next == head);}/* 将list链表所有元素拼到head链表的前面 */static inline void__list_splice (struct list_head *list, struct list_head *head){(list->prev)->next = (head->next);(head->next)->prev = (list->prev);(head)->next = (list->next);(list->next)->prev = (head);}/* 将list链表所有元素拼到head链表的前面 */static inline voidlist_splice (struct list_head *list, struct list_head *head){if (list_empty (list))return;__list_splice (list, head);}/* 将list链表所有元素拼到head链表的前面,并初始化list头结点 *//* Splice moves @list to the head of the list at @head. */static inline voidlist_splice_init (struct list_head *list, struct list_head *head){if (list_empty (list))return;__list_splice (list, head);INIT_LIST_HEAD (list);}/* 将list链表所有元素追加到head链表的后面 */static inline void__list_append (struct list_head *list, struct list_head *head){(head->prev)->next = (list->next);(list->next)->prev = (head->prev);(head->prev) = (list->prev);(list->prev)->next = head;}/* 将list链表所有元素追加到head链表的后面 */static inline voidlist_append (struct list_head *list, struct list_head *head){if (list_empty (list))return;__list_append (list, head);}/* 将list链表所有元素追加到head链表的后面,并初始化list *//* Append moves @list to the end of @head */static inline voidlist_append_init (struct list_head *list, struct list_head *head){if (list_empty (list))return;__list_append (list, head);INIT_LIST_HEAD (list);}/* 判断当前节点是否为最后一个节点 */static inline intlist_is_last (struct list_head *list, struct list_head *head){return (list->next == head);}/* 判断链表是否只有一个节点 */static inline intlist_is_singular(struct list_head *head){return !list_empty(head) && (head->next == head->prev);}/* 将旧节点用新节点替换 *//*** list_replace - replace old entry by new one* @old : the element to be replaced* @new : the new element to insert** If @old was empty, it will be overwritten.*/static inline void list_replace(struct list_head *old,struct list_head *new){new->next = old->next;new->next->prev = new;new->prev = old->prev;new->prev->next = new;}/* 将旧节点用新节点替换,并初始化旧节点 */static inline void list_replace_init(struct list_head *old,struct list_head *new){list_replace(old, new);INIT_LIST_HEAD(old);}/* 内核链表左旋 *//*** list_rotate_left - rotate the list to the left* @head: the head of the list*/static inline void list_rotate_left (struct list_head *head){struct list_head *first;if (!list_empty (head)) {first = head->next;list_move_tail (first, head);}}/* 根据链表节点地址找到数据元素首地址 */#define list_entry(ptr, type, member) \((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))/* 找到第一个数据元素地址 */#define list_first_entry(ptr, type, member) \list_entry((ptr)->next, type, member)/* 找到最后一个数据元素地址 */#define list_last_entry(ptr, type, member) \list_entry((ptr)->prev, type, member)/* 找到下一个数据元素地址 */#define list_next_entry(pos, member) \list_entry((pos)->member.next, typeof(*(pos)), member)/* 找到上一个数据元素地址 */#define list_prev_entry(pos, member) \list_entry((pos)->member.prev, typeof(*(pos)), member)
/* 遍历链表节点元素地址 */#define list_for_each(pos, head)
\for (pos = (head)->next; pos != (head); pos = pos->next)/* 遍历所有数据元素首地址 */#define list_for_each_entry(pos, head, member) \for (pos = list_entry((head)->next, typeof(*pos), member); \&pos->member != (head); \pos = list_entry(pos->member.next, typeof(*pos), member))/* 遍历所有数据元素首地址(可以在遍历过程中修改数据元素指针) */#define list_for_each_entry_safe(pos, n, head, member) \for (pos = list_entry((head)->next, typeof(*pos), member), \n = list_entry(pos->member.next, typeof(*pos), member); \&pos->member != (head); \pos = n, n = list_entry(n->member.next, typeof(*n), member))/* 倒着遍历所有数据元素首地址 */#define list_for_each_entry_reverse(pos, head, member)
\for (pos = list_entry((head)->prev, typeof(*pos), member); \&pos->member != (head); \pos = list_entry(pos->member.prev, typeof(*pos), member))/* 倒着遍历所有数据元素首地址(可以在遍历过程中修改数据元素指针) */#define list_for_each_entry_safe_reverse(pos, n, head, member)
\for (pos = list_entry((head)->prev, typeof(*pos), member), \n = list_entry(pos->member.prev, typeof(*pos), member); \&pos->member != (head); \pos = n, n = list_entry(n->member.prev, typeof(*n), member))/** This list implementation has some advantages, but one disadvantage:
you* can't use NULL to check whether you're at the head or tail. Thus,
the* address of the head has to be an argument for these macros.*//* 获得下一个数据元素空间首地址,如果没有返回NULL */#define list_next(ptr, head, type, member) \(((ptr)->member.next == head) ? NULL \: list_entry((ptr)->member.next, type,
member))/* 获得上一个数据元素空间首地址,如果没有返回NULL */#define list_prev(ptr, head, type, member) \(((ptr)->member.prev == head) ? NULL \: list_entry((ptr)->member.prev, type,
member))#endif /* _LLIST_H */