数据结构——链表变形

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

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

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; //新增尾指针
};
相关推荐
Nil20816 分钟前
leetcode 114二叉树展开为链表
leetcode·链表·深度优先
疯狂打码的少年1 小时前
【数据结构】板块总结 + 下期预告(数据库技术)
数据结构·笔记·算法
郝学胜-神的一滴1 小时前
Effective Python 条款 1:确认你正在使用的 Python 版本
开发语言·数据结构·python·程序人生·算法
dtq04243 小时前
数据结构 - 线性表 - 单链表
数据结构
青 春 记 忆3 小时前
LeetCode 234. 回文链表|Python 解法详解
python·leetcode·链表
1000世界小札3 小时前
排序算法全景总结:从 O(n²) 到 O(n log n),再到线性时间(附完整对比与选型指南)
数据结构·算法·排序算法
玉树临风ives4 小时前
atcoder ABC 472 题解
数据结构·c++·算法
李可以量化5 小时前
Redis Client 从入门到精通(一)下:redis-py 核心数据结构操作与连接管理
数据结构·redis·python·bootstrap·量化交易·qmt
疯狂打码的少年5 小时前
【数据结构】复习日:树 + 图 + 排序(整理对比表)
数据结构·笔记·算法