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

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

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;
}
目录
相关文章
|
8天前
|
Java
java数据结构,双向链表的实现
文章介绍了双向链表的实现,包括数据结构定义、插入和删除操作的代码实现,以及双向链表的其他操作方法,并提供了完整的Java代码实现。
java数据结构,双向链表的实现
|
1月前
|
存储 Java 索引
【数据结构】链表从实现到应用,保姆级攻略
本文详细介绍了链表这一重要数据结构。链表与数组不同,其元素在内存中非连续分布,通过指针连接。Java中链表常用于需动态添加或删除元素的场景。文章首先解释了单向链表的基本概念,包括节点定义及各种操作如插入、删除等的实现方法。随后介绍了双向链表,说明了其拥有前后两个指针的特点,并展示了相关操作的代码实现。最后,对比了ArrayList与LinkedList的不同之处,包括它们底层实现、时间复杂度以及适用场景等方面。
44 10
【数据结构】链表从实现到应用,保姆级攻略
|
26天前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
26天前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
|
2月前
|
存储 Java 开发者
揭秘!HashMap底层结构大起底:从数组到链表,再到红黑树,Java性能优化的秘密武器!
【8月更文挑战第24天】HashMap是Java集合框架中的核心组件,以其高效的键值对存储和快速访问能力广受开发者欢迎。在JDK 1.8及以后版本中,HashMap采用了数组+链表+红黑树的混合结构,实现了高性能的同时解决了哈希冲突问题。数组作为基石确保了快速定位;链表则用于处理哈希冲突;而当链表长度达到一定阈值时,通过转换为红黑树进一步提升性能。此外,HashMap还具备动态扩容机制,当负载因子超过预设值时自动扩大容量并重新哈希,确保整体性能。通过对HashMap底层结构的深入了解,我们可以更好地利用其优势解决实际开发中的问题。
64 0
|
2月前
|
存储 算法 测试技术
【初阶数据结构篇】实现顺序结构二叉树(堆的实现方法)
注意传过去的参数是插入的位置,即插入前的size,在调整完后再将size++
|
2月前
|
存储 算法
【初阶数据结构篇】顺序表和链表算法题
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
|
2月前
|
存储 测试技术
【初阶数据结构篇】双向链表的实现(赋源码)
因为头结点的存在,plist指针始终指向头结点,不会改变。
|
2月前
|
存储 测试技术
【初阶数据结构篇】单链表的实现(附源码)
在尾插/尾删中,都需要依据链表是否为空/链表是否多于一个节点来分情况讨论,目的是避免对空指针进行解引用造成的错误。
|
2月前
|
存储 缓存 算法
深入解析B树:数据结构、存储结构与算法优势
深入解析B树:数据结构、存储结构与算法优势