C++语言实现双向链表

这篇文章是关于利用C++模板的方式实现的双向链表以及双向链表的基本操作,在之前的博文C语言实现双向链表中,已经给大家分析了双向链表的结构,并以图示的方式给大家解释了双向链表的基本操作。本篇文章利用C++实现了双向链表的基本操作,其中包括:

双向链表的基本操作C++语言实现

双向链表 实现的功能
头部插入结点建立链表 尾部插入结点建立链表
实现指定位置插入结点 查找给定数值是否存在
删除指定位置的结点 修改指定位置的结点
双向链表的长度 打印双向链表

定义双向链表的结点

因为双向链表的结点由三部分构成,用于指向当前节点的直接前驱节点的指针域,用于存储数据元素数据域,以及用于指向当前节点的直接后继节点的指针域。

因此,首先我们需要封装一个结点类,定义了结点的三个要素,并利用构造函数实现初始化,另外,考虑到在双向链表中要用到结点类,所以将双向链表类定义为结点的友元类。

class doubleLinkedListNode
{
    private:
        doubleLinkedListNode<T> *prior;//双向结点前驱指针指向该结点的前驱结点
        T data;//储存结点数据
        doubleLinkedListNode<T> *next;//双向结点的后驱指针指向该结点的后继结点
        //将双向链表类定义为结点的友元类
        friend  class doubleLinkedList<T>;
    public:
        //结点的无参构造函数,将结点指针域初始化为NULL
        doubleLinkedListNode()
        {
            prior = NULL;
            next = NULL;
        }
        //结点的有参构造函数,初始化指针域和数据域
        doubleLinkedListNode(T _data,doubleLinkedListNode<T> *_prior = NULL,doubleLinkedListNode<T> *_next = NULL)
        {
            prior = _prior;//初始化前驱指针
            data = _data;//初始化数据域
            next = _next;//初始化后继指针
        }
        ~doubleLinkedListNode()
        {
            prior = NULL;
            next = NULL;
        }
};

双向链表的基本操作

实现了双向链表头部插入结点, 尾部插入结点,指定位置插入结点建立链表, 查找给定数值的指定位置,删除指定位置的结点,修改指定位置的结点,双向链表的长度,打印双向链表,接下来逐一进行讲解实现:

头部插入结点建立链表

带头结点实现的双向链表,实现头部插入结点可分为两种情况,一种是只有一个头结点的时候,只需要使head和newNode的两个指针关联上即可,另外的两个指针依旧是NULL状态。另一种情况便是有结点的情况,这个时候跟在中间结点插入相似,需要调整四个指针,首先是让newNode与后继结点关联,最后让newNode与head结点关联。
因此,头部插入结点实现如下:

template<class T>
bool doubleLinkedList<T>::insertNodeByhead(T item)
{
    //创建一个新的结点
    doubleLinkedListNode<T>* newNode = new doubleLinkedListNode<T>(item);
    if (newNode == NULL){
        cout << "内存分配失败,新结点无法创建" << endl;
        return false;
    }
    //分两种情况,head的next是否为NULL,然后处理四个指针
    if(head->next == NULL)
    {
        head->next = newNode;
        newNode->prior = head;
        return true;
    }
    else{
        newNode->next = head->next;
        head->next->prior = newNode;
        newNode->prior = head;
        head->next = newNode;
        return true;
    }
}

尾部插入结点建立链表

在尾部插入结点,当然第一步需要找到最后一个结点,然后在其后进行插入,双向链表因为两端的指针都是指向NULL的,所以在尾部插入也只需要调整两个指针就ok.

template<class T>
bool doubleLinkedList<T>::insertNodeBytail(T item)
{
    //创建一个新的结点
    doubleLinkedListNode<T>* newNode = new doubleLinkedListNode<T>(item);
    if (newNode == NULL){
        cout << "内存分配失败,新结点无法创建" << endl;
        return false;
    }
    //首先找到最后一个结点
    doubleLinkedListNode<T>* lastNode = head;
    while(lastNode->next != NULL)
    {
        lastNode = lastNode->next;//没找到就一直循环
    }
    //找到调整指针
    lastNode->next = newNode;
    newNode->prior = lastNode;
    return true;
}

实现指定位置插入结点

在指定位置插入只需要两步走,首先也是找到指定的位置,然后就是插入新结点的指针的调整,中间插入是最复杂的,都逃不过调整四个指针,但是首先依旧是让新结点和后继结点建立上相关性,最后让新结点与前继结点建立关系,实现新结点的插入。

bool doubleLinkedList<T>::insertNode(T item,int n)
{
    if(n<1){
        cout<<"输入的非有效位置!"<<endl;
        return false;
    }
    doubleLinkedListNode<T>* pMove = head;//创建一个新的指针,设置为游标指针
    //首先找到插入位置
    for(int i=1;i<n;i++)
    {
        pMove = pMove->next;
        if(pMove == NULL&& i<=n)
        {
            cout<<"插入位置无效!"<<endl;
            return false;
        }
    }
    //创建一个新的结点
    doubleLinkedListNode<T>* newNode = new doubleLinkedListNode<T>(item);
    if (newNode == NULL){
        cout << "内存分配失败,新结点无法创建" << endl;
        return false;
    }
    //插入新的结点
    newNode->next = pMove->next;   
    if (pMove->next != NULL)
    {
        pMove->next->prior = newNode;
    }
    newNode->prior = pMove;
    pMove->next = newNode;
    return true;
}

查找给定数值是否存在

查找给定元素,也就是一个遍历链表的过程,从头结点的下一个结点开始遍历,毕竟第一个头结点是没有储存数据项的。

template<class T>
bool doubleLinkedList<T>::findData(T item)
{
    doubleLinkedListNode<T> *pMove = head->next;  //设置游标指针
    if(pMove == NULL)//链表为空
    {
        return false;
    }
    while(pMove)//遍历链表
    {
        if(pMove->data == item){
            return true;
        }
        pMove = pMove->next;
    }
    return false;
}

删除指定位置的结点

删除指定的结点,第一步查找到删除的结点,需要定义一个删除指针临时指向将要删除的结点,最后指针处理删除之后别忘了释放该结点空间。

template<class T>
bool doubleLinkedList<T>::deleteData(int n)
{
    if (n<1||n>getLength())
    {
        cout << "输入非有效位置" << endl;
        return false;
    }
    doubleLinkedListNode<T> * pMove = head;//设置游标指针
    doubleLinkedListNode<T> * pDelete;             
    //查找删除结点的位置
    for (int i = 1; i <= n; i++)
    {
        pMove = pMove->next;                                         //游标指针后移
    }
    //删除结点
    pDelete = pMove;      
    pMove->prior->next = pDelete->next;
    pMove->next->prior = pDelete->prior;
    delete pDelete;//释放空间
    return true;
}

修改指定位置的结点

修改指定位置的结点数据,当然还是得找到指定位置,然后对其进行修改,修改之后将原来的数据以引用的形式返回,具体的用法在测试函数中写到了的,不会的可以作为参考。

template<class T>
bool doubleLinkedList<T>::changeListElements(int n,T item,T &x)
{
    if (n<1||n>getLength())
    {
        cout << "输入非有效位置" << endl;
        return false;
    }
    doubleLinkedListNode<T> *pMove = head->next;  //设置游标指针
    for(int i=1;i<n;i++)//找到指定位置1
    {
        pMove = pMove->next;
    }
    x = pMove->data;
    pMove->data = item;
    return true;
}

双向链表的长度

计算双向链表的长度的函数,在双向链表的私有成员封装了一个变量length,以此来记录双向链表的长度,遍历双向链表,逐一进行计算结点数就是双向链表的长度。

template<class T>
int doubleLinkedList<T>::getLength()
{
    doubleLinkedListNode<T> *pMove = head->next;  //设置游标指针
    int length=0;
    //遍历链表,计算结点数
    while(pMove!=NULL)
    {
        pMove = pMove->next;  //游标指针后移
        length++;       //计算length
    }
    return length;
}

打印双向链表

打印双向链表,从第二个结点开始遍历链表,因为第一个为头结点是不含数据的,打印的过程也就是一个遍历的过程。

template<class T>
void doubleLinkedList<T>::printLinkedlist()
{
    //从第二个结点开始打印,表头不含数据
    doubleLinkedListNode<T>* pMove = head->next;
    while(pMove)
    {
        cout<<pMove->data<<" ";
        pMove = pMove->next;//移动指针
    }
    cout<<endl;
}

以上就是本次博文与大家分享的利用C++语言实现双向链表,在用C语言写了之后,感觉写起来就比较轻松,唯一不同的就是要利用类来进行封装。完整的代码,以及测试代码我已经Push到Github,喜欢的小伙伴欢迎Star! C++语言实现双向链表Github地址,如果还想要了解其他的数据结构实现的小伙伴也可以来我的Myblog,我们一起讨论,如果有写的不好的地方还请多多担待,也欢迎大家在评论区留言,我加以改正,共同进步!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容

  • 关于双向链表的解释就不多说了,书本上介绍的挺详细的了,只需要记住每个节点有一个指向下一个节点的指针变量和指向前一个...
    小角色被占用阅读 1,252评论 0 0
  • C语言的标准库并没有实现链表,所以需要我们通过所学的知识来实现链表。先来看看双向链表的定义,来源于百度百科。 双向...
    zippozeng阅读 281评论 0 0
  • 目录 1、属性 2、链表和数组的区别 2.1、数组概述 2.2、数组和链表优缺点 2.3、链表和数组的比较 3、单...
    我哈啊哈啊哈阅读 2,802评论 1 41
  • 基本概念 链表的含义: 链表是一种用于存储数据集合的数据结构,具有以下属性 相邻元素之间通过指针相连 最后一个元素...
    古剑诛仙阅读 1,006评论 0 3
  • 链表(上):如何实现LRU缓存淘汰算法? 今天我们来聊聊“链表(Linked list)”这个数据结构。学习链表有...
    GhostintheCode阅读 1,328评论 0 4