数据结构之带头结点的双向循环链表(含全部代码)

简介: 数据结构之带头结点的双向循环链表(含全部代码)

带头结点的双向循环链表的实现

带头结点的双向循环链表和单链表相比具有许多的优点,增删查改更加的便捷,时间复杂度均为O(1)

头文件创建

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

定义数据类型以及创建链表结点

typedef int DataType;
typedef struct ListNode
{
  struct ListNode* prev;
  struct ListNode* next;
  DataType data;
}LN;

所需要实现的函数

//初始化
LN* ListInit();
//创建结点
LN* BuyListNode(DataType x);
//销毁链表
void ListDestory(LN* phead);
//打印链表
void ListPrint(LN* phead);
//头插
void ListPushFront(LN* phead, DataType x);
//尾插
void ListPushBack(LN* phead, DataType x);
//头删
void ListPopFront(LN* phead);
//尾删
void ListPopBack(LN* phead);
//查找x并返回x结点位置
LN* ListFind(LN* phead, DataType x);
//在pos前插入x
void ListInsert(LN* pos, DataType x);
//删除pos处的元素
void ListErase(LN* pos);
//判断链表是否为空
bool ListEmpty(LN* phead);
//计算元素个数
int ListSize(LN* phead);

函数的实现

#include"DList.h"
//初始化
LN* ListInit()
{
  LN* phead = BuyListNode(0);
  phead->next = phead;
  phead->prev = phead;
  return phead;
}
//创建结点
LN* BuyListNode(DataType x)
{
  LN* newnode = (LN*)malloc(sizeof(LN));
  if (newnode == NULL)
  {
    printf("malloc fail");
    exit(-1);
  }
  else
  {
    newnode->data = x;
    newnode->prev = NULL;
    newnode->next = NULL;
    return newnode;
  }
}
//销毁链表
void ListDestory(LN* phead)
{
  assert(phead);
  LN* cur = phead->next;
  while (cur != phead)
  {
    LN* next = cur->next;
    free(cur);
    cur = next;
  }
  free(phead);
  phead = NULL;
}
//打印链表
void ListPrint(LN* phead)
{
  assert(phead);
  LN* cur = phead->next;
  while (cur != phead)
  {
    printf("%d ", cur->data);
    cur = cur->next;
  }
  printf("\n");
}
//头插
void ListPushFront(LN* phead, DataType x)
{
  assert(phead);
  LN* newnode = BuyListNode(x);
  LN* first = phead->next;
  phead->next = newnode;
  newnode->prev = phead;
  newnode->next = first;
  first->prev = newnode;
}
//尾插
void ListPushBack(LN* phead, DataType x)
{
  assert(phead);
  LN* tail = phead->prev;
  LN* newnode = BuyListNode(x);
  tail->next = newnode;
  newnode->prev = tail;
  phead->prev = newnode;
  newnode->next = phead;
}
//头删
void ListPopFront(LN* phead)
{
  assert(phead);
  assert(phead->next != phead);
  LN* first = phead->next;
  phead->next = first->next;
  first->next->prev = phead;
  free(first);
  first = NULL;
}
//尾删
void ListPopBack(LN* phead)
{
  assert(phead);
  assert(phead->next != phead);
  LN* tail = phead->prev;
  //phead->prev = tail->prev;
  tail->prev->next = phead;
  phead->prev = tail->prev;
  free(tail);
  tail = NULL;
}
//查找x并返回x结点位置
LN* ListFind(LN* phead, DataType x)
{
  assert(phead);
  LN* cur = phead->next;
  while (cur != phead)
  {
    if (cur->data == x)
    {
      return cur;
    }
    cur = cur->next;
  }
  return NULL;
}
//在pos前插入x
void ListInsert(LN* pos, DataType x)
{
  assert(pos);
  LN* newnode = BuyListNode(x);
  LN* prev = pos->prev;
  prev->next = newnode;
  newnode->prev = prev;
  newnode->next = pos;
  pos->prev = newnode;
}
//删除pos处的元素
void ListErase(LN* pos)
{
  assert(pos);
  LN* prev = pos->prev;
  LN* next = pos->next;
  prev->next = next;
  next->prev = prev;
  free(pos);
  pos = NULL;
}
//判断链表是否为空
bool ListEmpty(LN* phead)
{
  if (phead == NULL)
  {
    return true;
  }
  else
  {
    return false;
  }
}
//计算元素个数
int ListSize(LN* phead)
{
  int size = 0;
  assert(phead);
  LN* cur = phead->next;
  while (cur != phead)
  {
    size++;
    cur = cur->next;
  }
  return size;
}
目录
相关文章
|
7月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
222 1
|
12月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
213 4
|
9月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
320 30
|
9月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
417 25
|
10月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
467 5
|
11月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
12月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
348 5
|
12月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
394 1
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
269 59
|
5月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
103 0
栈区的非法访问导致的死循环(x64)

热门文章

最新文章