数据结构循环双链表的完整代码复习

简介: 明天进军栈和队列,然后咱就开始刷题了

引言:

明天进军栈和队列,然后咱就开始刷题了

1.完整代码:

#include"标头.h"
//1.初始化
//因为此时我有对这个传过来的指针进行改变(进行了初始化)所以此时的头结点是发生的改变的,所以这边一定要有返回值
DLNode* ListInit()
{
  //因为我们是玩带哨兵位的,所以此时我们应该要先malloc出来一个结点(让这个结点成为我的哨兵位)
  //哨兵位头结点:(但是我函数外面的plist并拿不到这个哨兵位(也就是plist不会发生改变),所以需要有一个返回值来处理这个问题或者用二级指针也行)
  DLNode* phead = (DLNode*)malloc(sizeof(DLNode));
  //因为此时的结构是一个双向的循环,所以(此时结构体中的两个指针应该要有不同的指向)
  //一个指针指向自己
  phead->next = phead;
  //另一个指针也指向它自己
  phead->prev = phead;
  return phead;
}//以上就是对一个带头循环双向链表的初始化(这个就是C语言的魅力,什么都是靠自己来弄,你只是给了我一个概念的模型,剩下的东西都是要靠我自己来搞定这个结构应该是怎样的)
//2.尾插
//因为此时我的头结点plist传过来之后我并没有对其进行改变(因为我在初始化那步就已经改变过了,此时的这个plist就是已经拥有了两个指针(已经把哨兵位给创建好了),所以此时我在尾插时,就不会对plist进行任何形式的改变,所以此时我就不需要有返回值,当然也不需要有二进指针(这个也就是哨兵位的好处))
void ListPushBack(DLNode* phead, DSListType x)
{
  assert(phead);
  //DLNode* tail = phead->prev;//这步就是循环链表的大好处了(直接就可以找到最后一个结点的位置)
  //DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));//这个就是开辟新结点(尾插就一定要开辟一个新结点不然插什么)
  //newnode->data = x;
  以上就是一个准备工作,下面就是真正的双向的循环的原理实现(最好是附上一幅图)
  //tail->next = newnode;//这步的意思是因为此时的tail就是尾结点(就是让我的尾结点的最后一个指针去指向我新开辟出来的结点,这样就实现了尾插)
  //newnode->prev = tail;//这个就是为了实现双向循环链表(因为双向循环链表有两个指针,此时的一个指针就要指向刚刚那个尾结点(tail),只有这样我的newnode才可以取而代之)
  //newnode->next = phead;//然后此时的另一个指针就去指向我的头指针(为了实现循环,尾—>头)
  //phead->prev = newnode;//然后这步就是把刚刚的头的其中一个指针指向我的newnoode(还是为了循环)(头->尾),这样就实现了循环双指针链表
  //附用任意插
  ListInsert(phead, x);
}
//3.打印
//
void ListPrint(DLNode* phead)
{
  //因为此时phead中存的并不是一个有效的数据,所以此时不需要从头结点开始遍历(下一个结点开始)
  //判断结束条件就是通过:此时的这些数据在循环的过程之中最后会等于我的头结点(哨兵位),因为是循环的,所以不要以为它是一个循环就不能打印(只是停止的条件发生了一些的改变而已)
  assert(phead);
  DLNode* cur = phead->next;
  while (cur != phead)
  {
    printf("<-%d->", cur->data);
    cur = cur->next;
  }
}
//4.尾删(经典野指针错误)
//void ListPoptBack(DLNode* phead)
//{
//  DLNode* tail = phead->prev;
//  free(phead->prev);
//  tail->prev->next = phead;//这步就是那个道理,自己理解一下就行(先找前面那个,然后链接头结点)
//  phead->prev = tail->prev;
//
//}
//4.尾删(我的正确写法)
void ListPoptBack(DLNode* phead)
{
  assert(phead);
  assert(phead->next != phead);//这边就是说明我不可以把哨兵位给删掉(就是当phead->next=phead;时,此时就是代表我这个循环双链表就只剩下哨兵位自己了(因为这是一个循环的链表),此时就不能再进行删除了)
  首先肯定是不需要开辟新结点的
  //DLNode* tail = phead->prev;
  while (cur->next != tail)//找尾的上一个
  {
    cur = cur->next;
  }
   不敢把这个单链表的理解带过来我们的双链表(因为此时的整体的结构就是不一样的,因为此时的双链表是有两个指针的,一个是next指针,一个是prev指针,所以此时的尾的前一个只需要用prev这个指针去找就可以了)
  //DLNode* prev = tail->prev;
  //free(tail);
  //prev->next = NULL;
  此时以上只是大致的把尾给删除了,但是我还没有重新将这个循环链表给链接起来
  //prev->next = phead;
  //phead->prev = prev;
  //附用任意删
  ListDete(phead->prev);
}
//5.头插
void ListPushFront(DLNode* phead, DSListType x)
{
  assert(phead);
  //DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
  //newnode->data = x;
  //DLNode* tail = phead->next;
  //newnode->next = tail;
  //tail->prev = newnode;
  //newnode->prev = phead;
  //phead->next = newnode;
  //附用任意插
  ListInsert(phead->next, x);//这个位置一定要记住是phead->next,不然就会出问题,因为哨兵位的后一个才是头结点,所以在这个头结点前面插入才是我的头插
}
//6.头删
void ListPoptFront(DLNode* phead)
{
  //这边只要是与删除有关的代码,就要多断言一下,防止把哨兵位给删掉
  //assert(phead);
  //assert(phead->next != phead);//表示链表为空就不再需要断言了
  //
  //DLNode* tail = phead->next;
  //DLNode* next = tail->next;
  //free(tail);//这个free什么时候free就看你自己的方式了,可以最后free也可以后面free
  //next->prev = phead;
  //phead->next = next;
  //附用任意删
  ListDete(phead->next);
}
//7.查找
DLNode* ListFindname(DLNode* phead, DSListType x)//这个是为了找x,不敢当傻子了
{
  assert(phead);
  //这个的逻辑就有点像是print的逻辑,就是靠那个循环条件来完成
  DLNode* cur = phead->next;
  while (cur != phead)//这个循环是在保证我这个链表中不止只有哨兵位,而是有数据结点才开始找
  {
    if (cur->data == x)
    {
      return cur;
    }
    //这个位置就表示找到了
    cur = cur->next;
  }
  return NULL;
}
//8.任意位置插入(pos位置)
void ListInsert(DLNode* pos, DSListType x)//这个位置不一定要用pos,用一个int pos的下标也是一样的,但一定注意这个pos是一个DLNode结构体,里面是有两个指针和一个数据的
{
  assert(pos);
  DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
  newnode->data = x;
  DLNode* prev = pos->prev;
  newnode->prev = prev;
  prev->next = newnode;
  newnode->next = pos;
  pos->prev = newnode;
}
//9.任意位置删除(pos位置)
void ListDete( DLNode* pos)
{
  assert(pos);
  /*assert(phead->next != phead);*/
  DLNode* next = pos->next;
  DLNode* prev = pos->prev;
  free(pos);
  next->prev = prev;
  prev->next = next;
}
//10.销毁
void ListDestory(DLNode* phead)
{
  DLNode* cur = phead->next;
  DLNode* next = NULL;
  //while (cur->next != phead)//这步一开始你是这样写的,但是如果你写成这样的话,就会导致最后一个结点free不了,所以应该写成下面这样
  while (cur != phead)
  {
    //可以的怎么野指针怎么来(这就是我吗?)
    next = cur->next;
    free(cur);
    cur = NULL;
    cur = next;
  }
  //并且在你把所有的结点释放完之后(不能把哨兵位这个动态开辟的内存空间给忘记掉释放了),所以这边还要加一步
  free(phead);//但是这边要注意一个二级指针的问题(因为这步现在再改变我的函数外部的plist哨兵位)
  phead = NULL;
}

头文件

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DSListType;
typedef struct DSListNode
{
  DSListType data;
  struct DSListNode* next;
  struct DSListNode* prev;
}DLNode;
//以上就是一个高级结构体的创建(但是只是一个双向的概念),并不能体现出循环的概念
//1.初始化
DLNode* ListInit();
//2.打印
void ListPrint(DLNode* phead);
//3.尾插
void ListPushBack(DLNode* phead, DSListType x);
//4.尾删
void ListPoptBack(DLNode* phead);
//5.头插
void ListPushFront(DLNode* phead, DSListType x);
//6.头删
void ListPoptFront(DLNode* phead);
//7.查找
DLNode* ListFindname(DLNode* phead, DSListType x);
//8.销毁
void ListDestory(DLNode* phead);
//9.任意位置插入
void ListInsert(DLNode* pos, DSListType x);
//10.任意位置删除
void ListDete(DLNode* pos);
//11.上面的这两个函数是整个双向循环链表中最重要的


相关文章
|
3月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
78 4
|
7天前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
70 29
|
7天前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
64 25
|
1月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
37 5
|
2月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
3月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
94 5
|
3月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
93 1
|
3月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
157 4
|
3月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
67 0
|
3月前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
123 0

热门文章

最新文章