数据结构——链表变形

数据结构------链表变形

我们在上次已经了解了单链表,今天我们来了解一下链表的各种变形,如果还没有了解过上面单链表的小伙伴可以点击这里:

https://blog.csdn.net/qq_67693066/article/details/137640481

带尾指针的链表

这里我们介绍带头结点和带尾指针的链表

首先我们一开始的结点类基本不用改动:

cpp 复制代码
//结点类的定义
template<class T>
struct linkNode
{
    linkNode()
        :_data(T())
        ,_next(nullptr)
    {

    }

    linkNode(const T& data)
        :_data(data)
        ,_next(nullptr)
    {

    }

    //数据域
    T _data;

    //下一个结点的指针
    linkNode<T>* _next;
};

这是我们原先的链表结构:

cpp 复制代码
//链表类的定义
template<class T>
class list
{
    typedef linkNode<T> Node;
public:
    list()
    {
        _head = new Node();
    }

    //创建结点
    Node* createNode(const T& data)
    {
        //
        Node* newnode = new Node(data);

        if(newnode == nullptr)
        {
            perror("new fail");
            exit(EXIT_FAILURE);
        }

        return newnode;
    }

private:
    Node* _head;
};

这样我们创建一个链表的时候,默认就会有一个头结点:

现在我们想增加一个尾指针尾指针会指向最后一个元素 ,这个时候头结点就是最后一个元素,所以我们创建一个尾指针指向head:

同时在构造函数中,在开辟完头结点后,记得把尾指针指向头结点:

尾插的变化

尾插的时候,我们只需要在完成链接之后,将尾指针移动到新结点就行了:

cpp 复制代码
    //尾插
    void TailInsert(const T& data)
    {
        Node* newnode = createNode(data);

        _tail->_next = newnode;
        _tail = newnode;
    }

移动尾指针:

我们可以测试一下:

cpp 复制代码
#pragma once
#include<iostream>

//结点类的定义
template<class T>
struct linkNode
{
    linkNode()
        :_data(T())
        ,_next(nullptr)
    {

    }

    linkNode(const T& data)
        :_data(data)
        ,_next(nullptr)
    {

    }

    //数据域
    T _data;

    //下一个结点的指针
    linkNode<T>* _next;
};

//链表类的定义
template<class T>
class list
{
    typedef linkNode<T> Node;
public:
    list()
    {
        _head = new Node();
        _tail = _head;
    }

    //创建结点
    Node* createNode(const T& data)
    {
        //
        Node* newnode = new Node(data);

        if(newnode == nullptr)
        {
            perror("new fail");
            exit(EXIT_FAILURE);
        }

        return newnode;
    }

    //尾插
    void TailInsert(const T& data)
    {
        Node* newnode = createNode(data);

        _tail->_next = newnode;
        _tail = newnode;
    }

    //打印
    void PrintList()
    {
        Node* cur = _head->_next;

        while(cur != nullptr)
        {
            std::cout<< cur->_data << " ";
            cur=cur->_next;
        }

        std::cout<<"END"<<std::endl;
    }

private:
    Node* _head;
    Node* _tail; //新增尾指针
};
cpp 复制代码
#include "head.h"

int main()
{
    list<int> lt;

    lt.TailInsert(23);
    lt.TailInsert(1);
    lt.TailInsert(4);
    lt.TailInsert(100);

    lt.PrintList();

}

循环

我们现在想实现链表的循环,其实本质就是末尾结点的下一个结点就是头结点

就算只有头结点一个,也可以自己指向自己:

我们接着用尾指针的链表来改造:

打印结束的条件也应该修改:

cpp 复制代码
#pragma once
#include<iostream>

//结点类的定义
template<class T>
struct linkNode
{
    linkNode()
        :_data(T())
        ,_next(nullptr)
    {

    }

    linkNode(const T& data)
        :_data(data)
        ,_next(nullptr)
    {

    }

    //数据域
    T _data;

    //下一个结点的指针
    linkNode<T>* _next;
};

//链表类的定义
template<class T>
class list
{
    typedef linkNode<T> Node;
public:
    list()
    {
        _head = new Node();
        _tail = _head;
        _tail->_next = _head; //_tail此时指向_head的空间,可以控制_head所指的空间,这个操作是指向自己
    }

    //创建结点
    Node* createNode(const T& data)
    {
        //创建结点
        Node* newnode = new Node(data);

        if(newnode == nullptr)
        {
            perror("new fail");
            exit(EXIT_FAILURE);
        }

        return newnode;
    }

    //尾插
    void TailInsert(const T& data)
    {
        Node* newnode = createNode(data);

        _tail->_next = newnode;

        _tail = newnode;

        _tail->_next = _head; //最后一个结点的下一个结点指向头结点,完成循环
    }

    //打印
    void PrintList()
    {
        Node* cur = _head->_next;

        while(cur != _head)
        {
            std::cout<< cur->_data << " ";
            cur=cur->_next;
        }

        std::cout<<"END"<<std::endl;
    }

private:
    Node* _head;
    Node* _tail; //新增尾指针
};

双向

我们如果要完成双向,一个结点不能只存放后继结点,也要存放它的前驱结点:

所以我们的结点类要进行修改:

cpp 复制代码
//结点类的定义
template<class T>
struct linkNode
{
    linkNode()
        :_data(T())
        ,_next(nullptr)
    {

    }

    linkNode(const T& data)
        :_data(data)
        ,_next(nullptr)
    {

    }

    //数据域
    T _data;

    //下一个结点的指针
    linkNode<T>* _next;
    
    //前一个结点
    linkNode<T>* prve;
};

我们在有尾指针的基础上修改:

只有头结点的时候:

尾插的时候:

最后移动尾指针:

cpp 复制代码
#pragma once
#include<iostream>

//结点类的定义
template<class T>
struct linkNode
{
    linkNode()
        :_data(T())
        ,_next(nullptr)
    {

    }

    linkNode(const T& data)
        :_data(data)
        ,_next(nullptr)
    {

    }

    //数据域
    T _data;

    //下一个结点的指针
    linkNode<T>* _next;

    //前一个结点
    linkNode<T>* _prve;
};

//链表类的定义
template<class T>
class list
{
    typedef linkNode<T> Node;
public:
    list()
    {
        _head = new Node();
        _tail = _head;

        _tail->_prve = nullptr; //头结点没有前驱,_prve置为空
        _tail->_next = _head; //_tail此时指向_head的空间,可以控制_head所指的空间,这个操作是指向自己
    }

    //创建结点
    Node* createNode(const T& data)
    {
        //创建结点
        Node* newnode = new Node(data);

        if(newnode == nullptr)
        {
            perror("new fail");
            exit(EXIT_FAILURE);
        }

        return newnode;
    }

    //尾插
    void TailInsert(const T& data)
    {
        Node* newnode = createNode(data);

        _tail->_next = newnode; //修改原先最后结点的后继
        newnode->_prve = _tail; //修改新结点的前驱

        _tail = newnode;

    }

    //打印
    void PrintList()
    {
        Node* cur = _head->_next;

        while(cur != nullptr)
        {
            std::cout<< cur->_data << " ";
            cur=cur->_next;
        }

        std::cout<<"END"<<std::endl;

        //反向打印
        cur = _tail;
        while(cur != _head)
        {
            std::cout<< cur->_data << " ";
            cur=cur->_prve;
        }

        std::cout<<"HEAD"<<std::endl;
    }

private:
    Node* _head;
    Node* _tail; //新增尾指针
};

注意这里,实现双向循环之后,可以实现双向打印:

cpp 复制代码
    //打印
    void PrintList()
    {
        Node* cur = _head->_next;

        while(cur != nullptr)
        {
            std::cout<< cur->_data << " ";
            cur=cur->_next;
        }

        std::cout<<"END"<<std::endl;

        //反向打印
        cur = _tail;
        while(cur != _head)
        {
            std::cout<< cur->_data << " ";
            cur=cur->_prve;
        }

        std::cout<<"HEAD"<<std::endl;
    }

双向循环

双向循环就是处理了头结点的前驱和最后一个结点的后继:

在双向链表中:

头结点的前驱指向最后一个结点
最后一个结点的后继指向头结点

只有一个结点的时候,前驱和后继都指向自己:

在双向循环中,头结点储存了最后一个结点的信息,所以我们可以去掉尾指针。

cpp 复制代码
#pragma once
#include<iostream>

//结点类的定义
template<class T>
struct linkNode
{
    linkNode()
        :_data(T())
        ,_next(nullptr)
    {

    }

    linkNode(const T& data)
        :_data(data)
        ,_next(nullptr)
    {

    }

    //数据域
    T _data;

    //下一个结点的指针
    linkNode<T>* _next;

    //前一个结点
    linkNode<T>* _prev;
};

//链表类的定义
template<class T>
class list
{
    typedef linkNode<T> Node;
public:
    list()
    {
        _head = new Node();

        _head->_prev = _head; //头结点没有前驱,_prve置为空
        _head->_next = _head; 
    }

    //创建结点
    Node* createNode(const T& data)
    {
        //创建结点
        Node* newnode = new Node(data);

        if(newnode == nullptr)
        {
            perror("new fail");
            exit(EXIT_FAILURE);
        }

        return newnode;
    }

    //尾插
    void TailInsert(const T& data)
    {
        Node* newnode = createNode(data);

        newnode->_prev = _head->_prev; //修改新结点的前驱
        _head->_prev->_next = newnode; //修改原先最后结点的后继

        _head->_prev = newnode; //修改储存信息,指向最新的结点
        newnode->_next = _head; //新结点的后继指向头结点
    }

    //打印
    void PrintList()
    {
        Node* cur = _head->_next;

        while(cur != _head)
        {
            std::cout<< cur->_data << " ";
            cur=cur->_next;
        }

        std::cout<<"END"<<std::endl;

        //反向打印
        cur = _head->_prev;
        while(cur != _head)
        {
            std::cout<< cur->_data << " ";
            cur=cur->_prev;
        }

        std::cout<<"HEAD"<<std::endl;
    }

private:
    Node* _head;
    //Node* _tail; //新增尾指针
};
相关推荐
秋夫人16 分钟前
B+树(B+TREE)索引
数据结构·算法
代码雕刻家1 小时前
数据结构-3.1.栈的基本概念
c语言·开发语言·数据结构
AlexMercer10121 小时前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
^^为欢几何^^3 小时前
lodash中_.difference如何过滤数组
javascript·数据结构·算法
ahauedu4 小时前
案例分析-Stream List 中取出值最大的前 5 个和最小的 5 个值
数据结构·list
X同学的开始6 小时前
数据结构之二叉树遍历
数据结构
AIAdvocate9 小时前
Pandas_数据结构详解
数据结构·python·pandas
jiao000019 小时前
数据结构——队列
c语言·数据结构·算法
kaneki_lh9 小时前
数据结构 - 栈
数据结构
铁匠匠匠9 小时前
从零开始学数据结构系列之第六章《排序简介》
c语言·数据结构·经验分享·笔记·学习·开源·课程设计