当前位置: 首页 > news >正文

嵌入式学习 双向链表 循坏链表 内核链表

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;


应用场景

  1. 轮询调度:操作系统中的进程调度。
  2. 游戏开发:循环遍历角色或道具列表。
  3. 约瑟夫问题:经典循环链表求解问题。

注意事项

  1. 避免无限循环:遍历时需设置终止条件(如回到头节点)。
  2. 内存管理:动态分配节点后需手动释放内存。
  3. 头节点处理:插入/删除操作需维护循环性质。

通过合理设计,循环链表可高效处理环形数据逻辑问题。

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 */

http://www.dtcms.com/a/316939.html

相关文章:

  • Reading Books(Sorting and Searching)
  • Redis备份方案:持久化与外部工具全解析
  • G1系统概括
  • 电脑搜索不到公司无线网络
  • 【C# Winform】 Action事件驱动的多层数据传递
  • 【运维部署篇】OpenShift:企业级容器应用平台全面解析
  • 跑yolov5的train.py时,ImportError: Failed to initialize: Bad git executable.
  • Android 之 Kotlin中的kapt
  • io_uring系统调用及示例
  • Houdini Pyro学习笔记
  • [数组]977.有序数组的平方;209.长度最小的子数组
  • VUE+SPRINGBOOT从0-1打造前后端-前后台系统-邮箱重置密码
  • 数据结构——双向链表
  • 【学习嵌入式day-17-数据结构-单向链表/双向链表】
  • C语言:预处理、 库文件、 文件IO
  • Python深度学习:从入门到进阶
  • GPT-1、GPT-2、GPT-3 的区别和联系
  • C语言基础_IDE、进制转换、基本数据类型、输入输出函数、运算符
  • 一文搞定JavaServerPages基础,从0开始写一个登录与人数统计页面
  • 模拟面试总结
  • JSP相关Bug解决
  • Vue.js 教程
  • 市场与销售协同:CRM如何打破部门数据孤岛?
  • 思途Mybatis学习 0805
  • 一个小巧神奇的 USB数据线检测仪
  • LabVIEW 2025 安装攻略(附图文教程)适用于测试与自动控制领域
  • 亚马逊广告进阶指南:大词 VS 长尾词
  • 数据结构2.(双向链表,循环链表及内核链表)
  • 怎么在公司存活下去
  • SAP FI模块凭证增强逻辑的策略