数据结构|双向链表|带头结点|头插|尾插|尾删|头删

简介: 数据结构|双向链表|带头结点|头插|尾插|尾删|头删

双向链表的介绍

       双向链表是一种链表,它的每个节点都有两个链接,一个指向前一个节点,另一个指向下一个节点。相比于单向链表,双向链表在插入和删除操作时更加灵活,因为它们可以从两个方向进行操作。但是,双向链表的实现比单向链表更复杂,因为需要额外的指针来维护前一个和下一个节点的链接。

尾插

       在链表的尾部插入新的结点,要插入新的结点,首先得定义一个新的结点,接这找到链表尾部结点,最后通过改变指针指向进行插入。

       先定义一个新的结点(newnode),令他的next指向NULL;

       找链表的尾部结点,定义一个移动结点tail,通过next指针进行移动,不难看出tail的next指向NULL就是链表的尾部结点

要将新结点链接到双向链表上,只需要:

将最后一个链表的结点的next指针指向newnode上,

将newnode的prev指针指向双向链表的最后一个指针上。

这样尾插就完成了,下面是代码

// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
//定义新结点
  ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  if (newnode) {
    newnode->_data = x;
    newnode->_next = NULL;
  }
//找到最后一个结点
  ListNode* tail = pHead;
  while (tail->_next != NULL)
  {
    tail = tail->_next;
  }
//改变指针指向
  tail->_next = newnode;
  newnode->_prev=tail;
}

尾删

很简单,尾删就是删除最后一个节点将最后一个结点的位置置空:

首先应找到,最后一个结点

接着,最后一个结点的prev指向最后一个结点的前一个结点,

将前一个结点的next指向NULL((将最后一个结点的位置置空)

// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
//找到最后一个结点
  ListNode* tail = pHead;
  while (tail->_next!=NULL)
  {
    tail = tail->_next;
  }
  tail->_prev ->_next = NULL;
}

头插

       头插是在头结点的后边直接插入所以还是定一个新节点(newnode),通过改变头节点的指向,和头节点下一个结点的指向以及new结点的指向来完成头插。

       第一步先将newnode连接到链表上

       newnode->prev=pHead;

       newnode->next=pHead->next;

       因为头结点的下一个结点要通过pHead的next指针来访问,所以先不要改pHead的next的指向,否则就访问不到pHead的下一个结点了;

       要先将头结点的下一个结点的前驱结点指向newnode(pHead->next->prev=newnode)

       接着改pHead->next指针指向newnode(pHead->next=Newnode)

这样头插就完成了

// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x) 
{
  ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  if (newnode)
  {
    newnode->_data = x;
    newnode->_next = pHead->_next;
    newnode->_prev = pHead;
        pHead->_next->_prev=newnode;
    pHead->_next = newnode;
  }
}

头删

       头删就是删除头节点的下一个结点,还是同样的原因,因为要通过next指针找下一个结点,所以应该先改蓝色的指针,(pHead->next->next->prev=pHead)

接着pHead->next=pHead->next->next;

// 双向链表头删
void ListPopFront(ListNode* pHead)
{
  pHead->_next->_next->_prev = pHead;
  pHead->_next = pHead->_next->_next;
  
}

实现:

void test3()
{
  ListNode* Dhead = ListCreate();
  ListPushBack(Dhead, 1);
  ListPushBack(Dhead, 2);
  ListPushBack(Dhead, 3);
  ListPushBack(Dhead, 4);
  printf("1到4尾插:");
  ListPrint(Dhead);
  printf("\n");
  printf("尾删一次:");
  ListPopBack(Dhead);
  ListPrint(Dhead);
  printf("\n");
  printf("尾删两次:");
  ListPopBack(Dhead);
  ListPrint(Dhead);
  printf("\n");
  printf("\n");
  ListPushFront(Dhead, 7);
  ListPushFront(Dhead, 8);
  ListPushFront(Dhead, 9);
  printf("7到9头插:");
  ListPrint(Dhead);
  printf("\n");
  printf("头删一次:");
  ListPopFront(Dhead);
  ListPrint(Dhead);
  printf("\n");
  printf("头删两次:");
  ListPopFront(Dhead);
  ListPrint(Dhead);
  printf("\n"); 
}
int main()
{
  test3();
  return 0;
}

运行结果:

相关文章
|
1月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
54 4
|
23天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
44 5
|
1月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
48 0
|
1月前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
57 0
|
7月前
【移除链表元素】LeetCode第203题讲解
【移除链表元素】LeetCode第203题讲解
|
6月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
6月前
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表
|
6月前
|
存储 算法 Java
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
65 2
|
7月前
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点.
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点
61 1
|
6月前
|
算法
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表