双向链表基本操作

简介: 双向链表基本操作

前言:

往期给大家介绍了单链表, 单链表在 OJ 中出现频率较高,但实际使用的不多;而双链表是存储数据常用的表,它的全名是 带头双向循环链表 ,虽然结构复杂,但不一定复杂,甚至会比单链表容易实现。



Part1: 双向链表结构


所谓双向循环链表,就是相比单链表来说,一个节点中多了一个 结构体指针 指向前一个结点 罢了。

c66eef0a65e404fdabc2ed484b3e2896_7ea3ffea0689407f8f1a3886d673f880.png

我是图示


Part2: 相关功能实现


1.开工准备


1.1创建新结点


一个结点包含 要储存的元素 前一个结点的指针 下一个结点的指针

代码实现:

// 创建新结点
ListNode* BuyListNode(LTDataType x)
{
  ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  if (newnode == NULL)
  {
    perror("malloc fail");
    return NULL;
  }
  newnode->data = x;
  newnode->next = NULL;
  newnode->prev = NULL;
  return newnode;
}


1.2创建头节点


一个重要的问题是头结点中的两个指针指向哪里,指向空吗?

其实不是,这样就没有双向链表的特点了,

为了能准确体现双向链表的特点,要 先把头节点的两个指针都指向自己 ,这样就形成了闭环。

代码实现:

//创建头节点
ListNode* LTInit()
{
  ListNode* pHead = BuyListNode(-1);
  pHead->next = pHead;
  pHead->prev = pHead;
  return pHead;
}


2.相关功能实现


2.1链表打印

与单链表打印逻辑相近

代码实现:

// 双向链表打印
void ListPrint(ListNode* pHead)
{
  assert(pHead);
  ListNode* cur = pHead->next;
  printf("<=>pHead<=>");
  while (cur != pHead)
  {
    printf("%d<=>", cur->data);
    cur = cur->next;
  }
  printf("\n");
}


2.2尾部插入

与单链表相比,双向链表尾插不需要查找尾部,直接将头节点的前一个记录为尾即可。

剩下的就是修改四个指针。

代码实现:

// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
  assert(pHead);
  ListNode* tail = pHead->prev;
  ListNode* newnode = BuyListNode(x);
  tail->next = newnode;
  newnode->prev = tail;
  newnode->next = pHead;
  pHead->prev = newnode;
  //ListInsert(pHead, x);
}


2.3尾部删除


先记录,再修改指针,最后释放置空。

代码实现:

// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
  assert(pHead);
  ListNode* tail = pHead->prev;
  ListNode* tailprev = tail->prev;
  pHead->prev = tailprev;
  tailprev->next = pHead;
  free(tail);
  tail = NULL;
}


2.4头部插入


代码实现:

// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
  assert(pHead);
  ListNode* first = pHead->next;
  ListNode* newnode = BuyListNode(x);
  pHead->next = newnode;
  newnode->prev = pHead;
  newnode->next = first;
  first->prev = newnode;
}


2.5头部删除


代码实现:

// 双向链表头删
void ListPopFront(ListNode* pHead)
{
  assert(pHead);
  ListNode* first = pHead->next;
  ListNode* firstnext = first->next;
  pHead->next = firstnext;
  firstnext->prev = pHead;
  free(first);
  first = NULL;
}


2.6查找位置


这个也与单链表的位置查找相近

代码实现:

// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
  assert(pHead);
  ListNode* cur = pHead;
  while (cur && cur->data != x)
  {
    cur = cur->next;
  }
  return cur;
}


2.7在pos前插入


代码实现:

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
  assert(pos);
  ListNode* newnode = BuyListNode(x);
  ListNode* posprev = pos->prev;
  posprev->next = newnode;
  newnode->prev = posprev;
  newnode->next = pos;
  pos->prev = newnode;
}


2.8删除pos位置的结点


代码实现:

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
  assert(pos);
  ListNode* posprev = pos->prev;
  ListNode* posnext = pos->next;
  posprev->next = posnext;
  posnext = posprev;
  free(pos);
  pos = NULL;
}


2.9销毁链表


代码实现:

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
  assert(pos);
  ListNode* posprev = pos->prev;
  ListNode* posnext = pos->next;
  posprev->next = posnext;
  posnext = posprev;
  free(pos);
  pos = NULL;
}

总结:

在学习完单链表后再进行双链表的实现可谓是水到渠成,所以有些地方就没做详细解释,这也告诉我们复杂的东西不一定难实现。

目录
相关文章
|
缓存 安全 网络协议
socket是并发安全的吗 2
socket是并发安全的吗
779 0
|
5月前
|
人工智能 安全 API
用Qwen Code,体验全新AI编程——高效模型接入首选ModelGate
Qwen Code 是通义千问推出的AI编程助手,支持自然语言编程与智能代码生成,大幅提升开发效率。结合 ModelGate,可实现多模型统一管理、安全调用,解决API切换、权限控制、稳定性等问题,是Claude Code的理想国产替代方案。
|
存储 缓存 前端开发
【数据结构/C语言】深入理解 双向链表
【数据结构/C语言】深入理解 双向链表
|
机器学习/深度学习 人工智能 自然语言处理
【人工智能技术专题】「入门到精通系列教程」零基础带你进军人工智能领域的全流程技术体系和实战指南(NLP自然语言处理概念介绍)
【人工智能技术专题】「入门到精通系列教程」零基础带你进军人工智能领域的全流程技术体系和实战指南(NLP自然语言处理概念介绍)
377 0
|
7月前
|
Go
【LeetCode 热题100】155:最小栈(详细解析)(Go语言版)
本文详细解析了力扣热题155:最小栈的解题思路与实现方法。题目要求设计一个支持 push、核心思路是使用辅助栈法,通过两个栈(主栈和辅助栈)来维护当前栈中的最小值。具体操作包括:push 时同步更新辅助栈,pop 时检查是否需要弹出辅助栈的栈顶,getMin 时直接返回辅助栈的栈顶。文章还提供了 Go 语言的实现代码,并对复杂度进行了分析。此外,还介绍了单栈 + 差值记录法的进阶思路,并总结了常见易错点,如 pop 操作时忘记同步弹出辅助栈等。
223 6
|
JSON JavaScript Java
对比JSON和Hessian2的序列化格式
通过以上对比分析,希望能够帮助开发者在不同场景下选择最适合的序列化格式,提高系统的整体性能和可维护性。
404 3
|
存储 安全 区块链
WBTC与BTC的主要区别
WBTC与BTC的主要区别
860 6
|
运维 监控 Serverless
探索Serverless高可用架构:云上极简运维的新篇章
随着云计算的快速发展,Serverless 架构因其无需管理服务器、按需自动扩展等优势,逐渐成为企业应用构建的重要选择。阿里云提供的 Serverless 高可用架构解决方案,通过结合多种云服务,提供了强大的高可用性和自动化运维能力。本文将评测阿里云 Serverless 高可用架构的核心功能、优势及其应用场景,帮助读者更好地理解和使用这一解决方案。
|
Ubuntu 前端开发 Shell
Linux apt命令详解
1.apt简介 apt(Advanced Packaging Tool)是一个在 Debian 和 Ubuntu 中的 Shell 前端软件包管理器。 apt 命令提供了查找、安装、升级、删除某一个、一组甚至全部软件包的命令,而且命令简洁而又好记。 apt 命令执行需要超级管理员权限(root)。
1036 0