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

双向链表实现(c语言)

链表是数据结构中的基础内容,其中双向链表(Doubly Linked List)相比单向链表增加了一个前驱指针,因此可以向前和向后遍历,删除节点时也更加方便。

双向链表的结构如下:

typedef struct Node { int data; struct Node* pre; struct Node* next; }Node;

实现如下逻辑:

创建节点方式:

Node* createNode(int value) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = value; newNode->pre = NULL; newNode->next = NULL; return newNode; }

头插法:

每次把新节点放在链表前面

void insertFront(Node** head, int value) { Node* newNode = createNode(value); newNode->next = *head; if (*head != NULL) { (*head)->pre = newNode; } *head = newNode; }

我们来看图示:

创建第一个节点, 在main()函数中有Node* head = NULL;(稍后介绍main()函数的内容),

newNode从createNode()函数返回接收首地址(用0x1代替0x000 000 000 000 000 1,懒得写太长的十六进制数了)

第一轮因为是空链表,所以不执行if判断

将head解引用赋值为newNode,也就是地址

增加新节点的时候如下:

执行if判断,形成指向即为链表,

同时head头指针指向新节点

尾插法:

尾插需要先找到最后一个节点

void insertEnd(Node** head, int value) { Node* newNode = createNode(value); if (*head == NULL) { *head = newNode; return; } Node* temp = *head; //获得地址后解引用next,循环找到next指针区域为NULL,即为末尾节点 while(temp->next != NULL) { temp = temp->next; } //temp记录了原来的末尾节点,设置temp的next指针区域指向新的末尾节点为newNode temp->next = newNode; //新的末尾节点newNode的pre指针区域指向原来的末尾节点 newNode->pre = temp; }

我们来看图示:

创建第一个节点的时候,执行if判断将head头指针指向第一个节点

创建第二个节点的时候,用临时指针接收头指针

获得地址后解引用next,循环找到next指针区域为NULL,即为末尾节点

temp记录了原来的末尾节点,设置temp的next指针区域指向新的末尾节点为newNode

新的末尾节点newNode的pre指针区域指向原来的末尾节点

删除节点:

删除过程只需要修改前后两个节点的指针即可

void deleteNode(Node** head, int value) { Node* temp = *head; while (temp != NULL && temp->data != value) { temp = temp->next; } if (temp == NULL) { return; } //跳跃 if (temp->pre != NULL) { temp->pre->next = temp->next; } //如果删除的是第一个节点,将头指针设置为第二个节点 else { *head = temp->next; } if (temp->next != NULL) { temp->next->pre = temp->pre; } free(temp); }

最后free(temp);

最后释放内存空间:

void destory(Node* head) { while(head != NULL) { Node* temp = head; head = head->next; free(temp); } }

正向遍历和反向遍历方法:

void printForward(Node* head) { while(head != NULL) { printf("%d ", head->data); head = head->next; } printf("\n"); } void printBackWard(Node* head) { if (head == NULL) return; while (head->next != NULL) { head = head->next; } while (head != NULL) { printf("%d ", head->data); head = head->pre; } printf("\n"); }

完整代码如下:

#include<stdio.h> #include<stdlib.h> typedef struct Node { int data; struct Node* pre; struct Node* next; }Node; Node* createNode(int value) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = value; newNode->pre = NULL; newNode->next = NULL; return newNode; } void insertFront(Node** head, int value) { Node* newNode = createNode(value); newNode->next = *head; if (*head != NULL) { (*head)->pre = newNode; } *head = newNode; } void insertEnd(Node** head, int value) { Node* newNode = createNode(value); if (*head == NULL) { *head = newNode; return; } Node* temp = *head; //获得地址后解引用next,循环找到next指针区域为NULL,即为末尾节点 while(temp->next != NULL) { temp = temp->next; } //temp记录了原来的末尾节点,设置temp的next指针区域指向新的末尾节点为newNode temp->next = newNode; //新的末尾节点newNode的pre指针区域指向原来的末尾节点 newNode->pre = temp; } void deleteNode(Node** head, int value) { Node* temp = *head; while (temp != NULL && temp->data != value) { temp = temp->next; } if (temp == NULL) { return; } //跳跃 if (temp->pre != NULL) { temp->pre->next = temp->next; } //如果删除的是第一个节点,将头指针设置为第二个节点 else { *head = temp->next; } if (temp->next != NULL) { temp->next->pre = temp->pre; } free(temp); } void printForward(Node* head) { while(head != NULL) { printf("%d ", head->data); head = head->next; } printf("\n"); } void printBackWard(Node* head) { if (head == NULL) return; while (head->next != NULL) { head = head->next; } while (head != NULL) { printf("%d ", head->data); head = head->pre; } printf("\n"); } void destory(Node* head) { while(head != NULL) { Node* temp = head; head = head->next; free(temp); } } int main() { Node* head = NULL; insertFront(&head, 10); insertFront(&head, 20); insertFront(&head, 30); insertFront(&head, 40); printForward(head); printBackWard(head); deleteNode(&head, 30); printForward(head); printBackWard(head); destory(head); }

运行结果如下:

http://www.jsqmd.com/news/1171478/

相关文章:

  • 感受“冲天火箭”的函数
  • RAID卡故障排查实战:5种常见报错现象与对应的3步定位修复流程
  • Python时间序列可视化:从画图到诊断的工程实践
  • LLM应用六层纵深防御体系:从输入净化到输出水印
  • 【RAG工程化天花板】:基于LangChain v0.1.18+LlamaIndex 0.10.5双框架对比,独家披露92%团队忽略的缓存穿透防护设计
  • Wireshark 4.0 实战:二层交换网络3类拓扑VLAN通信与广播域隔离分析
  • AlphaZero核心技术解析:蒙特卡洛树搜索与卷积神经网络协同实现自我对弈学习
  • 2026湖南GEO网站定制开发公司最新推荐:实地探访聚之唯,拆解AI时代官网开发的技术内幕
  • ESP32 ESP-NOW vs WiFi 对比评测:5项关键指标实测与3大应用场景选型
  • 【Android Performance】Swappiness参数原理与内存回收策略深度解析
  • Runway Dev一站式AI媒体生成平台:企业级集成与实战指南
  • Leaflet 离线地图 3 大性能优化:从瓦片加载策略到 Vue 组件封装
  • UE5风格化环境制作:材质、光照与后期处理实战指南
  • Harness AI工程化实战:从零构建生产级AI Agent完整指南
  • webdriver-manager 自动管理 ChromeDriver:告别 2 步手动下载与版本匹配
  • 从 AI 辅助到 AI 原生:软件工程团队的开发范式变化
  • YOLO与Transformer核心原理及工程实践:从代码实现到生产部署
  • YOLOv8目标检测实战:从环境配置到工业部署完整指南
  • Cache 高速缓存原理:从SRAM选型到3级缓存命中率提升策略
  • Spring Boot性能测试与优化实战:从工具选型到JVM调优
  • Seedream 5.0 Pro多模态图像生成模型:从技术原理到数据可视化实战
  • Java简历包装(42期)
  • 机器人顶会ICRA/IROS 2025投稿指南:从RA-L到主会的3种路径与避坑要点
  • Startup数据科学实战:零基建、高存活的生存指南
  • 深入解析printf系统调用:从用户态到内核态的完整执行路径
  • Windows 防火墙入站/出站规则优先级解析:5个典型场景下的冲突与解决
  • AI API隐形守门人CGL:安全层如何变成最大不安全源
  • 蓝桥杯 2024 省赛 3 大高频考点精讲:DFS、DP与同余定理实战拆解
  • Pandas数据清洗三阶漏斗模型:从结构到业务的完整实践
  • 影刀RPA 从手工操作到自动化的需求分析方法