【数据结构初阶】双向带头循环链表原来是纸老虎,结构复杂,操作简单

简介: 【数据结构初阶】双向带头循环链表原来是纸老虎,结构复杂,操作简单

6ec171f1317b488ca896a71ae27c57f6.png

双向带头循环链表:结构复杂,操作简单

0.结构体定义

这里方便浏览,特地没有将int类型重命名为TLDateType

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
struct ListNode
{
  int val;
  struct ListNode* prev;
  struct ListNode* next;
};

1.初始化

  1. 带头需要初始化一个哨兵位头结点
  2. 传二级指针
  3. 初始化自己的prev和next都指向自己
void ListInit(struct ListNode** pphead)
{
  struct ListNode* Guard = (struct ListNode*)malloc(sizeof(struct ListNode));
  if (Guard == NULL)
  {
    perror("ListInit");
    return;
  }
  *pphead = Guard;
  Guard->next = Guard;
  Guard->prev = Guard;
}

2.尾插

  1. 一级指针
  2. 直接prev找尾
  3. 即是无首元结点,也可(头结点自环)
struct ListNode* BuyListNode(int x)
{
  struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
  if (newnode == NULL)
  {
    perror("BuySListNode");
    return NULL;
  }
  newnode->val = x;
  newnode->prev = NULL;
  newnode->next = NULL;
  return newnode;
}
void ListPushBack(struct ListNode* phead,int x)
{
  assert(phead);
  struct ListNode* newnode = BuyListNode(x);
  struct ListNode* tail = phead->prev;
  newnode->next = phead;
  phead->prev = newnode;
  tail->next = newnode;
  newnode->prev = tail;
}

3.打印

  1. 可assert断言(建议单链表不用断言)---头结点
  2. 起始条件cur=phead->next;
  3. 循环终止条件cur==phead
void ListPrint(struct ListNode* phead)
{
  assert(phead);
  struct ListNode* cur = phead->next;
  while (cur != phead)
  {
    printf("%d->", cur->val);
    cur = cur->next;
  }
  printf("NULL\n");
}

4.头插

  1. 一级指针
  2. 即是无首元结点,也可(头结点自环)
void ListPushFront(struct ListNode* phead,int x)
{
  assert(phead);
  struct ListNode* newnode = BuyListNode(x);
  struct ListNode* next = phead->next;
  newnode->next = next;
  next->prev = newnode;
  phead->next = newnode;
  newnode->prev = phead;
}

5.任意位置插入前面位置

  1. 因为有prev,前插不同phead
  2. 尾插,头插就用ListInsert传不同pos参数
  3. 如果pos传phead,就相当于于是尾插
  4. 如果pos传phead->next,就相当于于是头插
void ListInsert(struct ListNode* pos, int x)
{
  assert(pos);
  struct ListNode* newnode = BuyListNode(x);
  struct ListNode* prev = pos->prev;
  prev->next = newnode;
  newnode->prev = prev;
  newnode->next = pos;
  pos->prev = newnode;
}
头插:ListNode(phead->next,x);
尾插:ListNode(phead,x);

6.尾删

  1. 不为空,!ListEmpty(plhead)
  2. prev,tail,phead
bool ListEmpty(struct ListNode* phead)
{
  return phead->next == phead;
}
void ListPopBack(struct ListNode* phead)
{
  assert(phead);
  assert(!ListEmpty(phead));
  struct ListNode* tail = phead->prev;
  struct ListNode* prev = tail->prev;
  prev->next = phead;
  phead->prev = prev;
  free(tail);
  tail = NULL;
}

7.头删

  1. 不为空,!ListEmpty(plhead)
  2. phead,first,second
void ListPopFront(struct ListNode* phead)
{
  assert(phead);
  assert(!ListEmpty(phead));
  struct ListNode* first = phead->next;
  struct ListNode* second = first->next;
  phead->next = second;
  second->prev = phead;
  free(first);
  first = NULL;
}

8.链表长度

  1. 起始条件:cur=phead->next
  2. 终止条件:cur!=phead;
size_t ListSize(struct ListNode* phead)
{
  size_t size = 0;
  struct ListNode* cur = phead->next;
  while (cur != phead)
  {
    ++size;
    cur = cur->next;
  }
  return size;
}

9.任意位置删除当前位置

  1. 不为空,!ListEmpty(plhead)
  2. 尾删,头删就用ListErase传不同pos参数
  3. 如果pos传phead->prev,就是尾删
  4. 如果pos传phead->next,就是头删
void ListErase(struct ListNode* pos)
{
  assert(pos);
  struct ListNode* prev = pos->prev;
  struct ListNode* next = pos->next;
  prev->next = next;
  next->prev = prev;
  free(pos);
  pos = NULL;
}

10. 销毁

  1. 看是否保留哨兵头,来传一级或二级指针
  2. 先保留哨兵头作为判断循环条件,最后决定是否释放哨兵头
void ListDestory(struct ListNode** pphead)
{
  assert(pphead);
  struct ListNode* cur = (*pphead)->next;
  while (cur != *pphead)
  {
    struct ListNode* next = cur->next;
    free(cur);
    cur = next;
  }
  free(*pphead);
  *pphead = NULL;
}
目录
相关文章
|
18天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
60 16
|
21天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
48 4
|
22天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
22天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
算法 Java
数据结构与算法学习五:双链表的增、删、改、查
双链表的增、删、改、查操作及其Java实现,并通过实例演示了双向链表的优势和应用。
17 0
数据结构与算法学习五:双链表的增、删、改、查
|
21天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
39 0
|
1月前
|
存储
[数据结构] -- 双向循环链表
[数据结构] -- 双向循环链表
24 0
|
1月前
探索顺序结构:栈的实现方式
探索顺序结构:栈的实现方式
|
1月前
|
存储
探索数据结构:便捷的双向链表
探索数据结构:便捷的双向链表
|
1月前
|
存储
探索数据结构:单链表的实践和应用
探索数据结构:单链表的实践和应用