C++实现单链表

C++实现单链表

链表是一种以链式存储的线性表,由于内存空间可以不连续,使用十分灵活,与数组相比各有优缺点。

单链表需要有个标识指明起始位置以便于操作整个链表,可以是头指针也可以是头结点,只是实现上的不同,这里使用头结点的方式。

在这里插入图片描述

头结点内存结构和数据节点相同,只是头结点不保存数据。有了头结点之后,要操作的第一个数据节点就是头结点的下一个节点。

    struct Node
    {
        T data;
        Node* next;
    };

	//通过定义与头结点相同内存布局的头节点变量,避免操作头结点数据,使用 &m_header 即获得指向头结点的指针
    struct
    {
        char reserved[ sizeof(T) ];
        Node* next;
    } m_header;

主要实现接口:

position(int i)节点指针定位
insert(int i, const T& e)指定位置插入
insert(const T& e)从尾部插入
remove(int i)删除第i个节点
get(int i, T& e)获取第i个节点
get (int i)获取第i个节点const版本
traverse()遍历整个链表

如何在链表任一位置插入节点

假设除了头结点之外,有两个数据节点,那么可插入位置为三个
在这里插入图片描述

假设要在图中标识为0的位置插入节点,则插入位置是头结点的next节点(重要

同理在1的位置插入节点,则插入位置是头结点的next->next节点

同理在2的位置插入节点,则插入位置是头结点的next->next->next节点……

于是得出规律:

  • 在第0个位置插入,直接跟在头结点后
  • 在第1个位置插入,就从头结点跳过1个节点后插入
  • 在第2个位置插入,就从头结点跳过2个节点后插入……以此类推

节点指针定位

根据上一节的分析,在第i个位置插入节点,需要跳过i个节点,由此定位待插入位置的前一个节点

    Node* position(int i)
    {
        Node* ret = reinterpret_cast<Node*>(&m_header);

        for ( int p = 0; p < i; p++ )
        {
            ret = ret->next;
        }

        return ret;
    }

插入函数

通过position函数得到目标位置的前一个位置后,就可以执行插入操作了,位置范围[0, m_length]
在这里插入图片描述

    bool insert(int i, const T& e)
    {
        bool ret = ( 0 <= i && i <= m_length );

        if( ret )
        {
            Node* node = new (std::nothrow) Node();
            assert(nullptr != node);

            Node* current = position(i);

            node->data = e;
            node->next = current->next;
            current->next = node;

            m_length++;
        }
        return ret;
    }

	//尾插重载版本
    bool insert(const T& e)
    {
        return insert(m_length, e);
    }

删除函数

同理删除节点时,也可以通过指针定位的方式,有点不同的是,删除时位置范围只有[0, m_length - 1]
在这里插入图片描述

    bool remove(int i)
    {
        bool ret = ( 0 <= i && i <m_length );

        if (ret)
        {
            Node* current = position(i);
            Node *toDel = current->next;

            current->next = toDel->next;
            m_length--;

            delete toDel;
        }

        return ret;
    }

访问第i个节点

    T get (int i) const
    {
        T ret;

        if ( get(i, ret) )
        {
            return ret;
        }
        else
        {
            throw std::out_of_range("get list element out of range ...");
        }
    }

    bool get(int i, T& e)
    {
        bool ret = ( (0 <= i) && (i < m_length) );

        if (ret)
        {
            e = position(i)->next->data;
        }

        return ret;
    }

遍历整个链表

    void traverse()
    {
        Node* current = position(1);
        while( current )
        {
            std::cout << current->data << " ";
            current = current->next;
        }
        std::cout << std::endl;
    }
#include <iostream>
#include <cassert>

template<typename T>
class List
{
    struct Node
    {
        T data;
        Node* next;
    };

    struct
    {
        char reserved[ sizeof (T) ];
        Node* next;
    } m_header;

    int m_length;

    Node* position(int i)
    {
        Node* ret = reinterpret_cast<Node*>(&m_header);

        for ( int p = 0; p < i; p++ )
        {
            ret = ret->next;
        }

        return ret;
    }

public:
    List()
    {
        m_header.next = nullptr;
        m_length = 0;
    }

    bool insert(const T& e)
    {
        return insert(m_length, e);
    }

    bool insert(int i, const T& e)
    {
        bool ret = ( 0 <= i && i <= m_length );

        if( ret )
        {
            Node* node = new (std::nothrow) Node();
            assert(nullptr != node);

            Node* current = position(i);

            node->data = e;
            node->next = current->next;
            current->next = node;

            m_length++;
        }
        return ret;
    }

    bool remove(int i)
    {
        bool ret = ( 0 <= i && i < m_length );

        if (ret)
        {
            Node* current = position(i);
            Node *toDel = current->next;

            current->next = toDel->next;
            m_length--;

            delete toDel;
        }

        return ret;
    }

    T get (int i) const
    {
        T ret;

        if ( get(i, ret) )
        {
            return ret;
        }
        else
        {
            throw std::out_of_range("get list element out of range ...");
        }
    }

    bool get(int i, T& e)
    {
        bool ret = ( (0 <= i) && (i < m_length) );

        if (ret)
        {
            e = position(i)->next->data;
        }

        return ret;
    }

    void traverse()
    {
        Node* current = position(1);
        while( current )
        {
            std::cout << current->data << " ";
            current = current->next;
        }
        std::cout << std::endl;
    }

    int length() const
    {
        return m_length;
    }

    void clear()
    {
        while (m_header.next)
        {
            Node* toDel = m_header.next;
            m_header.next = toDel->next;
            m_length--;
            delete toDel;
        }
    }
};

int main()
{
    List<int> list;

    for ( int i = 0; i < 5; ++i )
    {
        list.insert(i);
    }

    list.traverse();//0 1 2 3 4

    list.insert(3, 100);

    list.traverse();//0 1 2 100 3 4

    list.remove(0);
    list.remove(2);

    list.traverse();//1 2 3 4

    list.clear();

    list.traverse();//空

    return 0;
}
//运行结果
0 1 2 3 4 
0 1 2 100 3 4 
1 2 3 4 

参考

  • 狄泰软件学院:数据结构实战开发
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值