数据结构(二)——线性表(双链表)

这篇具有很好参考价值的文章主要介绍了数据结构(二)——线性表(双链表)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

2.3.3 双链表

单链表:单链表结点中只有一个指向其后继的指针,使得单链表只能从前往后依次遍历,无法逆向检索,有时候不太方便

双链表的定义:双链表结点中有两个指针prior和next,分别指向其直接前驱和直接后继
表头结点的prior域和尾结点的next域都是NULL。
数据结构(二)——线性表(双链表),考研408,数据结构

双链表结点类型描述如下:

typedef struct DNode{            //定义双链表结点类型
    ElemType data;                //数据域
    struct DNode *prior, *next;    //前驱和后继指针
} DNode, *DLinklist;

双链表在单链表结点中增加了一个指向其前驱的指针 prior

双链表的初始化 (带头结点):

typedef struct DNode{       //D表示double
    ElemType data;     
    struct DNode *prior, *next;
}DNode, *DLinklist;

// 初始化双链表
bool InitDLinkList(Dlinklist &L){     
    L = (DNode *)malloc(sizeof(DNode));     //分配一个头结点
    if(L==NULL)            //内存不足,分配失败
        return false;    
    L->prior = NULL;   //头结点的prior指针永远指向NULL     
    L->next = NULL;    //头结点之后暂时还没有结点,置空   
    return true;
}

void testDLinkList(){  
    DLinklist L;       //L是一个链表  初始化双链表
    InitDLinkList(L);       
    ...
}

// 判断双链表是否为空
bool Empty(DLinklist L){   
    if(L->next == NULL)   
        return true;      
    else             
        return false;
}

双链表的插入操作: 

typedef struct DNode{     
    ElemType data;       
    struct DNode *prior, *next;
}DNode, *DLinklist;

bool InsertNextDNode(DNode *p, DNode *s){ 
    if(p==NULL || s==NULL)  
        return false;         
    s->next = p->next;       // 将结点s插入到结点p之后
    if (p->next != NULL)       // 判断结点p之后是否有后继结点  
        p->next->prior = s; //p结点的后置结点的前向指针指向新插入的s
    s->prior = p;   //把s的前向指针指向p结点
    p->next = s;     //把p结点的后向指针指向s结点
    return true;
}

双链表的删除操作:

// 删除p结点的后继结点
bool DeletNextDNode(DNode *p){   
    if(p==NULL)           
        return false;      
    DNode *q =p->next;       // 找到p的后继结点q 
    if(q==NULL)          //p没有后继就返回false
        return false;    
    p->next = q->next;   //q结点不是最后一个结点
    if(q->next != NULL) 
        q->next->prior=p;  
    free(q);             //释放结点空间
    return true;
}

// 销毁一个双链表
bool DestoryList(DLinklist &L){ 
    // 循环释放各个数据结点   
    while(L->next != NULL){    
        DeletNextDNode(L);      
        free(L);            //释放头结点
        L=NULL;             // 头指针指向NULL
    }
}

 双链表的遍历:

// 向后遍历
while(p!=NULL){    
    // 对结点p做相应处理    
    p = p->next;
}

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

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

双链表不可随机存取,按位查找、按值查找操作都只能用遍历的方式实现。时间复杂度O(n)
数据结构(二)——线性表(双链表),考研408,数据结构

2.3.4 循环链表

循环单链表

数据结构(二)——线性表(双链表),考研408,数据结构
数据结构(二)——线性表(双链表),考研408,数据结构

循环单链表和单链表的区别在于,表中最后一个结点的指针不是NULL,而改为指向头结点,从而整个链表形成一个环
在循环单链表中,表尾结点*r的next域指向L,故表中没有指针域为NULL的结点,因此,循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针L。 

循环单链表的实现:

typedef struct LNode{           //定义单链表结点类型
    ElemType data;                  
    struct LNode *next;     //指针指向下一个元素
}DNode, *Linklist;

// 初始化循环单链表
bool InitList(LinkList &L){    
    L = (LNode *)malloc(sizeof(LNode));      //分配一个头结点
    if(L==NULL)             //内存不足,分配失败
        return false;    
    L->next = L;           // 最后一个结点的next指针指向头结点    
    return true;
}

// 判断循环单链表是否为空
bool Empty(LinkList L){    
    if(L->next == L)       
        return true;    
    else             
        return false;
}

// 判断结点p是否为循环单链表的表尾结点
bool isTail(LinkList L, LNode *p){ 
    if(p->next == L)          //看p结点的下一个结点是否是头结点
        return true;      
    else            
        return false;
}

单链表:从一个结点出发只能找到后续的各个结点
循环单链表:从一个结点出发可以找到其他任何一个结点 

循环双链表
数据结构(二)——线性表(双链表),考研408,数据结构

循环双链表的初始化

typedef struct DNode{            
    ElemType data;           
    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指针指向头结点 
}

void testDLinkList(){
    //初始化循环双链表
    DLinkList L;
    InitDLinkList(L);
    //...
}

// 判断循环双链表是否为空
bool Empty(DLinklist L){   
    if(L->next == L)       
        return true;      
    else           
        return false;
}

// 判断结点p是否为循环双链表的表尾结点
bool isTail(DLinklist L, DNode *p){   
    if(p->next == L)        //结点的next指针指向头结点
        return true;     
    else            
        return false;
}

循环双链表的插入 

// 将结点s插入到结点p之后
bool InsertNextDNode(DNode *p, DNode *s){  
    s->next = p->next;   
    //循环双链表不用担心p结点的下一个结点为空   
    p->next->prior = s;  
    s->prior = p;     
    p->next = s;
}

  循环双链表的删除

// 删除p结点的后继结点
bool DeletNextDNode(DNode *p){  
    // 找到p的后继结点q       
    DNode *q =p->next;        
    //循环双链表不用担心q结点的下一个结点为空  
    p->next = q->next;    
    q->next->prior=p;    
    free(q);      
    return true;
}

2.3.5 静态链表

静态链表:用数组的方式实现的链表

优点:增、删操作不需要大量移动元素
缺点:不能随机存取,只能从头结点开始依次往后查找,容量固定不可变

适用场景:
①不支持指针的低级语言;
②数据元素数量固定不变的场景(如操作系统的文件分配表FAT)

数据结构(二)——线性表(双链表),考研408,数据结构
静态链表是用数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,与前面所讲的链表中的指针不同的是,这里的指针是结点在数组中的相对地址(数组下标),又称游标。

静态链表的定义

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

// 用数组定义多个连续存放的结点
void testSLinkList(){    
    struct Node a[MaxSize];  //数组a作为静态链表, 每一个数组元素的类型都是struct Node    
    ...
}

 王道书上是这么定义的,侧重于强调 a 是一个静态链表而非数组。

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

void testSLinkList(){      
    SLinkList a;
}

静态链表基本操作实现

初始化静态链表:
把a[0]的next 设为-1
把其他结点的next 设为一个特殊值用来表示结点空闲,如-2

查找结点:
从头结点出发挨个往后遍历结点

插入位序为 i 的结点:
①找到一个空的结点,存入数据元素
②从头结点出发找到位序为 i-1 的结点
③修改新结点的 next
④修改 i-1 号结点的 next

删除某个结点:
①从头结点出发找到前驱结点
②修改前驱结点的游标
③被删除结点 next 设为 -2文章来源地址https://www.toymoban.com/news/detail-838570.html

到了这里,关于数据结构(二)——线性表(双链表)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构入门精讲 | 第十六篇】并查集知识点及考研408、企业面试练习

    上一篇中我们进行了散列表的相关练习,在这一篇中我们要学习的是并查集。 在许多实际应用场景中,我们需要对元素进行分组,并且在这些分组中进行查询和修改操作。比如,在图论中,我们需要将节点按照连通性进行分组,以便进行最小生成树、最短路径等算法;在计算

    2024年02月03日
    浏览(49)
  • 【玩转408数据结构】线性表——线性表的顺序表示(顺序表)

            通过前文,我们了解到线性表是具有相同数据类型的有限个数据元素序列;并且,线性表只是一种逻辑结构,其不同存储形式所展现出的也略有不同,那么今天我们来了解一下线性表的顺序存储——顺序表。         顺序表指的是将 逻辑上相邻的元素 存储在 物理位

    2024年02月19日
    浏览(51)
  • 【玩转408数据结构】线性表——定义和基本操作

            线性表是算法题命题的重点,该类题目实现相对容易且代码量不高,但需要最优的性能(也就是其时间复杂度以及空间复杂度最优),这样才可以获得满分。所以在考研复习中,我们需要掌握线性表的基本操作,在平时多进行代码练习。当然在考场上,我们并不一

    2024年02月19日
    浏览(46)
  • 数据结构--线性表(顺序表、单链表、双链表、循环链表、静态链表)

    前言  学习所记录,如果能对你有帮助,那就泰裤辣。 目录 1.线性表概念 定义 基本操作 2.顺序表 定义 顺序表的实现--静态分配 动态分配 顺序表的特点 顺序表的插入和删除 顺序表的查找 按位查找  按值查找 3.单链表 定义 单链表的初始化 不带头节点的单链表 带头节点的单

    2024年02月11日
    浏览(61)
  • 【玩转408数据结构】线性表——单链表的定义以及增删改查(线性表的链式表示 上)

            到这里我们已经了解到线性表是具有 相同数据类型 的 有限个数据元素 序列,而线性表的顺序存储也就是顺序表,顺序表的存储形式十分直观,我们在实现时使用数组进行实现,但顺序表在插入或者删除元素时需要移动大量元素,那么怎么样才能在插入删除元素时不

    2024年02月21日
    浏览(54)
  • 24考研数据结构-线性表4

    2.4.4单链表的查找操作(默认带头节点,不带头节点后续更新) 2.4.4.1 按位查找操作 GetElem(L, i): 按位查找操作,获取表L中第i个位置的元素的值; 注意: 1.边界情况 i=0,返回头节点;iL.length,返回null; 2.ji即查找到j = i 的节点,就是第i个节点。 3.平均复杂度O(n) 2.4.4.2 按值

    2024年02月16日
    浏览(40)
  • 【考研复习】24王道数据结构课后习题代码|2.3线性表的链式表示

    删除结点:1、2、4 就地逆置:5、 合并链表 分解链表:10、11、 去除重复元素:12、 并集:14、15 循环链表:17、18、19、20 头插法、尾插法重点基础必掌握。 判断是否有环:21 用函数递归调用删除结点。 注意删除结点的时候可能断链。 利用函数调用的特性反向输出。 设置保

    2024年02月13日
    浏览(44)
  • 数据结构--双链表

    单链表 VS 双链表 单链表:无法逆向检索,有时候不太方便 双链表:可进可退,存储密度更低一丢丢 在p结点之后插入s结点 特殊处理:p是最后一个结点 用后插操作实现结点的插入有什么好处? 按位序插入前插操作: 找到前一个结点进行后插操作 ####删除p的后继结点q 特殊处

    2024年02月11日
    浏览(39)
  • 【数据结构】双链表

     带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。 1.准备工作  由于实际开发项目中,项目的实现都是采用模

    2024年02月14日
    浏览(38)
  • 数据结构——双链表

    我宁愿靠自己的力量,打开我的前途,而不愿求有力者垂青  文章目录 双线向链表各接口函数名或变量名  双向链表接口实现源码 快速索引【头文件及函数声明】 双向链表接口实现 双向链表的构造分析 双向链表的定义及初始化 双向链表的插入和删除 往期回顾: 数据结构

    2024年02月11日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包