数据结构——链表变形

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

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

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; //新增尾指针
};
相关推荐
抠脚学代码3 小时前
Linux开发-->驱动开发-->字符设备驱动框架
linux·数据结构·驱动开发
星释6 小时前
Rust 练习册 8:链表实现与所有权管理
开发语言·链表·rust
熬了夜的程序员6 小时前
【LeetCode】101. 对称二叉树
算法·leetcode·链表·职场和发展·矩阵
橘颂TA7 小时前
【剑斩OFFER】算法的暴力美学——串联所有单词的字串
数据结构·算法·c/c++
葵续浅笑8 小时前
LeetCode - 杨辉三角 / 二叉树的最大深度
java·数据结构·算法·leetcode
Miraitowa_cheems8 小时前
LeetCode算法日记 - Day 94: 最长的斐波那契子序列的长度
java·数据结构·算法·leetcode·深度优先·动态规划
.ZGR.9 小时前
第十六届蓝桥杯省赛 C 组——Java题解1(链表知识点)
java·算法·链表·蓝桥杯
Paxon Zhang11 小时前
数据结构之**二叉树**超全秘籍宝典2
java·数据结构·算法
迷途之人不知返12 小时前
链表相关的算法题(2)
数据结构·算法·链表
nju_spy12 小时前
力扣每日一题(四)线段树 + 树状数组 + 差分
数据结构·python·算法·leetcode·面试·线段树·笔试