【数据结构】顺序表和链表2(下)

简介: 【数据结构】顺序表和链表2(下)

5.打印链表

定义cur指针遍历链表,逐个访问数据,这里为了更加形象,在每个数据之后加上了->符号。

void SListPrint(SListNode* plist)
{
  SListNode* cur = plist;
  while (cur != NULL)
  {
    printf("%d->", cur->data);
    cur = cur->next;
  }
  printf("NULL\n");
}


6.查找数据

遍历链表,逐个与需要查找到数据对比,如果与我们需要查找到数据相同,就返回该节点地址,否则返回NULL

SListNode* SListFind(SListNode* plist, SLTDateType x)
{
  SListNode* cur = plist;
  while (cur)
  {
    if (cur->data == x)
    {
      return cur;
    }
    cur = cur->next;
  }
  return NULL;
}


(2)带头双向循环链表的接口实现

1.存储结构

双向链表和单列表的区别就是双向链表多一个指向前一个节点的指针prev,结构如下图:

12d50f487e8345b5a921833a4397721f.png

typedef struct ListNode
{
  LTDataType _data;
  struct ListNode* _next;
  struct ListNode* _prev;
}ListNode;

2.初始化与销毁

  • 初始化

带头链表的初始化就是新建一个哨兵位头节点,后面对链表进行操作的时候直接传头节点即可,此时链表内没有元素,所有哨兵位的prev指向自己,next也指向自己。

ListNode* ListCreate()
{
  ListNode* guard = (ListNode*)malloc(sizeof(ListNode));
  if (guard == NULL)
  {
    perror("BuyListNode");
    exit(-1);
  }
  guard->_next = guard;
  guard->_prev = guard;
  return guard;
}


  • 销毁

由于增加节点的时候是一个一个的malloc的所有销毁的时候也是一个一个的释放,那么定义一个cur指针,遍历链表,依次free,最后free掉哨兵位。

void ListDestory(ListNode* pHead)
{
  assert(pHead);
  ListNode* cur = pHead->_next;
  ListNode* next = NULL;
  while (cur != pHead)
  {
    next = cur->_next;
    free(cur);
    cur = next;
  }
  free(pHead);
    pHead = NULL;
}


3.插入数据

每一次插入数据都需要创建新节点,因此封装一个BuyListNode函数,用于创建新节点

ListNode* BuyListNode(LTDataType x)
{
  ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  if (newnode == NULL)
  {
    perror("BuyListNode");
    exit(-1);
  }
  newnode->_next = NULL;
  newnode->_prev = NULL;
  newnode->_data = x;
  return newnode;
}


  • 头插

创建新节点,链接在guard的下一个节点位置。将nownode的prev指向guard。

void ListPushFront(ListNode* pHead, LTDataType x)
{
  assert(pHead);
  ListNode* newnode = BuyListNode(x);
  newnode->_next = pHead->_next;
  pHead->_next->_prev = newnode;
  pHead->_next = newnode;
  newnode->_prev = pHead;
}


  • 尾插

在单链表里,进行尾插的时候,需要先找到尾,但是对于双向链表,他的guard节点指向的prev就是尾节点,就可以省略了找尾节点的过程,然后将newnode节点链接上即可

void ListPushBack(ListNode* pHead, LTDataType x)
{
  assert(pHead);
  ListNode* newnode = BuyListNode(x);
  ListNode* tail = pHead->_prev;
  tail->_next = newnode;
  newnode->_prev = tail;
  pHead->_prev = newnode;
  newnode->_next = pHead;
}


  • pos位置之前插入

在pos位置之前插入就需要在用用prev记录pos的前一个节点,然后创建一个新节点,并链接在链表上即可。

void ListInsert(ListNode* pos, LTDataType x)
{
  assert(pos);
  ListNode* prev = pos->_prev;
  ListNode* newnode = BuyListNode(x);
  prev->_next = newnode;
  newnode->_prev = prev;
  newnode->_next = pos;
  pos->_prev = newnode;
}


4.删除数据

在删除节点的时候,要保证删除的不能是哨兵位节点,所以这里封装一个判空函数判断链表里是否只有一个头节点,返回bool值。

bool ListEmpty(ListNode* pHead)
{
  assert(pHead);
  return pHead->_next == pHead;
}


  • 头删

首先要判断传入的指针是否有效,链表是否为空,头删删除的是哨兵位的下一节点,那么我们定义一个del保存guard的next节点,然后将del节点取下来,将其他guard与del的下一节点链接起来,然后释放del节点。

void ListPopFront(ListNode* pHead)
{
  assert(pHead);
  assert(!ListEmpty(pHead));
  ListNode* del = pHead->_next;
  pHead->_next = del->_next;
  del->_next = pHead;
  free(del);
  del = NULL;
}


  • 尾删

首先判断指针有效性,然后判空。尾删本质上删除的是guard的前一个节点,所以我们先定义一个tail保存尾节点,然后取下来tail节点,将其他的节点链接起来,然后释放tail节点。

void ListPopBack(ListNode* pHead)
{
  assert(pHead);
  assert(!ListEmpty(pHead));
  ListNode* tail = pHead->_prev;
  tail->_prev->_next = pHead;
  pHead->_prev = tail->_prev;
  free(tail);
  tail = NULL;
}


  • 删除pos位置的节点

删除该节点,直接将pos位置的节点的前一个节点和后一个节点链接起来,然后释放pos位置即可。

void ListErase(ListNode* pos)
{
  assert(pos);
  pos->_prev->_next = pos->_next;
  pos->_next->_prev = pos->_prev;
  free(pos);
  pos = NULL;
}


5.查找数据

遍历链表,并与需要查找的数据对比,匹配之后返回该节点地址,否则返回空指针。

ListNode* ListFind(ListNode* pHead, LTDataType x)
{
  assert(pHead);
  ListNode* cur = pHead->_next;
  while (cur != pHead)
  {
    if (cur->_data == x)
      return cur;
    cur = cur->_next;
  }
  return NULL;
}


6.打印数据

遍历链表,依次打印数据,这里为了更加形象,在每个数据后面加上'<=>'符号。

void ListPrint(ListNode* pHead)
{
  assert(pHead);
  ListNode* cur = pHead->_next;
  printf("guard<=>");
  while (cur != pHead)
  {
    printf("%d<=>", cur->_data);
    cur = cur->_next;
  }
  printf("\n");
}


相关文章
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
280 4
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
469 30
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
668 25
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
604 5
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】顺序表的基本运算(头歌实践教学平台习题)【合集】
本文档介绍了线性表的基本运算任务,涵盖顺序表和链表的初始化、销毁、判定是否为空、求长度、输出、查找元素、插入和删除元素等内容。通过C++代码示例详细展示了每一步骤的具体实现方法,并提供了测试说明和通关代码。 主要内容包括: - **任务描述**:实现顺序表的基本运算。 - **相关知识**:介绍线性表的基本概念及操作,如初始化、销毁、判定是否为空表等。 - **具体操作**:详述顺序表和链表的初始化、求长度、输出、查找、插入和删除元素的方法,并附有代码示例。 - **测试说明**:提供测试输入和预期输出,确保代码正确性。 - **通关代码**:给出完整的C++代码实现,帮助完成任务。 文档
434 5
|
存储
顺序表和链表(2)
【10月更文挑战第23天】
259 2
顺序表和链表(2)
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
478 5
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
391 0
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
371 59