C语言双链表,循环链表,静态链表讲解(王道版)

简介: 目录一、双链表初始化(带头结点):双链表的插入:双链表的遍历 循环链表 循环单链表的初始化循环双链表的初始化双链表的插入双链表的删除静态链表定义静态链表插入删除

目录

一、双链表

初始化(带头结点):

双链表的插入:

双链表的遍历

循环链表

循环单链表的初始化

循环双链表的初始化

双链表的插入

双链表的删除

静态链表

定义静态链表

插入

删除


双链表

在单链表中,每个元素都附加了一个指针域,指向下一个元素的存储位置。在双向链表中,每个元素都附加了两个指针域,分别指向前驱节点和后继节点。


单链表只能向后操作,不能向前操作。为了向前、向后操作方便,可以给每个元素都附加两个指针域,一个存储前一个元素的地址,一个存储下一个元素的地址。这种链表被称为双向链表示。


从上图中可以看出,双向链表的每个节点都包含三个域:数据域和两个指针域。两个指针域分别存储前后两个元素的地址,即指向前驱节点和后继节点。


初始化(带头结点):

typedef struct DNode{              //定义双链表结点类型 
  Elemtype date;                //数据域 
  struct DNode *prior,*next;    //前驱和后继指针 
}DNode,*DLinklist;
//初始化双链表
bool InitDLinkList(DLinklist &L){
  L =(DNode*)malloc(sizeof(DNode));
  if(L==NULL)
    return false;
  L->prior = NULL;
  L->next = NULL; 
} 

判断链表是否为空(带头结点)

bool Empty(DLinklist L){
  if(L->next == NULL)
     return true;
    else
       return false;
}

双链表的插入:

//在p结点之后插入s结点
bool InsertDNode(DNode *p,DNode *s)
{
  if(p==NULL||s==NULL)
    return false;
  s->next = p->next;
  if(p->next != NULL)     //如果在尾部插入,尾部是NULL,空的不能指向
    p->next->prior = s;
  s->prior= p;
  p->next = s;
  return true;
} 


双链表的遍历

后序遍历

while(P!=NULL)
{
   //对结点p处理如打印
   p = p -> next;
}

前序遍历

while(p!=NULL){
   //对结点p做相应的处理
   p = p -> prior;
}

前向遍历(跳过头结点)

while(p->prior!=NULL)
{
   //对结点p做相应的处理
   p = p-> prior;
}

双链表不可随机存取,按位查找和按值查找都只能用遍历的方式实现。时间复杂度O(n)


循环链表

在单链表中,只能向后操作,不能向前操作,如果从当前节点开始,则无法访问该节点前面的节点;


如果最后一个节点的指针指向头节点,形成一个环,就可以从任何一个节点出发,访问所有节点,这就是循环链表。


循环链表和普通链表的区别就是最后一个节点的后继指向了头节点。下面看看单链表和单向循环链表的区别。


单向循环链表最后一个节点的next域不为空,而是指向了头节点,


而单链表和单向循环链表判断空表的条件也发生了变化,单链表为空表时,L ->next=NULL;单向循环链表为空表时,L ->next=L 。


双向循环链表除了要让最后一个节点的后继指向第1个节点,还要让头节点的前驱指向最后一个节点

image.png

双向循环链表为空表时,L ->next=L ->prior=L ,如下图所示。

image.png

循环单链表的初始化

typedef struct LNode{           //定义单链表的结点类型 
  ElemType date;              //每个结点存放一个数据元素 
  struct LNode *next;         //指针指向下一个结点 
}LNode,*LinkList;
//初始化一个循环单链表
bool InitList(LinkList &L) {
  L = (LNode*) malloc(sizeof(LNode)); //分配一个头结点
  if(L==NULL)               //内存不足分配失败 
      return false;      
    L->next = L;            //头接点next指向头结点 
  return true; 
}

循环双链表的初始化

//初始化一个循环双链表
typedef struct DNode{
  ElemType date;
  struct DNode *prior,*next;
}DNode,*DLinklist;
bool InitDLinkList(DLinklist &L){
  L = (DNode *) malloc(sizeof(DNode)); //分配一个头结点
  if(L==NULL)
     return false;
  L->prior = L;    //头结点的prior指向头结点 
  L->next = L;     //头结点的next指向头结点 
  return true; 
} 

此时判断是否为空和判断是否为尾结点的条件就是看next是否为L

双链表的插入

由于是循环双链表,就不用考虑是不是在尾部插入

//在p结点之后插入s结点
bool InsertDNode(DNode *p,DNode *s){
  s->next= p->next;
  p->next->prior= s;
  s->prior = p;
  p->next = s; 
} 

双链表的删除

//删除p的后继结点q
p->next = q->next;
q->next->prior = p;
free(q);

静态链表

链表还有另一种静态表示方式,可以用一个数组存储数据,用另一个数组记录当前数据的后继的下标。

用静态链表可以先把数据存储在一维数组data[]中,然后用后继数组next[]记录每个元素的后继下标

定义静态链表

#define Maxsize 10      //静态链表的最大长度
struct Node{            //静态链表的结构类型定义
  ElemType date;       //存储数据元素
  int next;             //下一个元素数组下标
}SLinkList[MaxSize];

插入为i的结点:


1)找到一个空的结点存入数据元素


2)从头结点出发找到位序为i-1的结点


3) 修改新的结点next


4)修改i -1 的结点next


插入

若在第6个元素之前插入一个元素25,则只需将25放入data[]数组的尾部,即data[9]=25,然后修改后继数组next[5]=9,next[9]=6



插入之后,next[5]=9,next[9]=6,也就是说节点5的后继为9,节点9的后继为6,节点6的前驱为9,节点9的后继为6


相当于节点9被插入节点5和节点6之间,即插入节点6之前。也就是说,元素49的后继为25,元素25的后继为20。这就相当于把元素25插入49、20之间。是不是也很方便?不需要移动元素,只改动后继数组就可以了。


删除

若删除第3个元素,则只需修改后继数组next[2]=4,。此时,2的后继为4,相当于把第3个元素跳过去了,实现了删除功能,而第3个元素并未被真正删除,只是它已不在链表中。这样做的好处是不需要移动大量的元素。


相关文章
|
28天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
54 4
|
29天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
2月前
|
存储 缓存 C语言
C语言:链表和数组有什么区别
C语言中,链表和数组是两种常用的数据结构。数组是一种线性结构,元素在内存中连续存储,通过下标访问,适合随机访问且大小固定的情况。链表由一系列不连续的节点组成,每个节点存储数据和指向下一个节点的指针,适用于频繁插入和删除操作的场景,链表的大小可以动态变化。
|
2月前
|
C语言
无头链表再封装方式实现 (C语言描述)
如何在C语言中实现无头链表的再封装,包括创建节点和链表、插入和删除操作、查找和打印链表以及销毁链表的函数。
29 0
|
2月前
|
C语言
C语言链式结构之有头单链表再封装写法
本文介绍了如何使用C语言对有头单链表进行封装,包括节点的创建、链表的初始化、数据的插入和删除,以及链表的打印等功能。
19 1
|
2月前
|
C语言
C语言结构体链式结构之有头单链表
文章提供了一个C语言实现的有头单链表的完整代码,包括创建链表、插入、删除和打印等基本操作。
31 1
|
28天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
41 0
|
2月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
29 0
|
3月前
|
C语言
C语言里的循环链表
C语言里的循环链表