数据结构之线性表(C++) -- 链式描述

数据结构之线性表 – 链式描述


概述

与数组描述不同,在链式描述中,线性表的元素在内存中的存储位置是随机的。每个元素都有一个明确的指针(链)指向线性表的下一个元素的位置(地址)

链表的种类

一般分为单链表,双链表和循环链表(单向或双向)。

单链表

单链表结构是每个节点只有一个链,例如
单链表

双链表

链表中每个节点有两个链,一个指向上一个节点,一个指向下一个节点

循环链表

循环链表是单链表的一种变种,单链表是将尾节点置为NULL,而循环链表是将尾节点指向头结点。

链表的节点结构

这里我们以单链表为例,构建它的结构,如上图所示一个单链表需要有数据域(用来存放元素),链域(用来指向下一个节点),所以代码如下:

// 定义节点的结构
template <typename T>
struct chainNode
{
    T element;
    chainNode<T> *next;

    chainNode() {}
    chainNode(const T& element)
    {
        this->element = element;
    }

    chainNode(const T& element, chainNode<T>* next)
    {
        this->element = element;
        this->next = next;
    }
};

链表的类(class chain)

当我们定义一个单链表的时候,我们考虑到要如何创建,如何添加,如何删除等等操作,因此编写代码如下。

// 链表的操作
template <typename T>
class chain
{
public:
    chain();
    chain(const chain<T>&);
    ~chain();
    //ADT实现
    void add(int element);      // 向尾端添加元素
    bool empty() const { return listSize == 0; }    // 判断是否为空
    int size() const { return listSize; }           // 返回链表长度
    T& get(int theIndex) const;                     // 根据下标获取链表元素
    int indexof(const T& theElement) const;         // 根据元素获取下标
    void erase(int theIndex);                       // 根据下标删除元素
    void insert(int theIndex, const T& theElement); // 插入操作
    void output() const;                            // 输出链表

private:
    bool checkIndex(int theIndex)   // 检查下标
    {
        if(theIndex < 0 || theIndex > listSize)
            return false;
        return true;
    }

    chainNode<T>* firstNode;
    int listSize;
};

链表类的实现

构造函数和析构函数

template <typename T>
chain<T>::chain()
{
    firstNode = NULL;
    listSize = 0;
}

template <typename T>
chain<T>::chain(const chain<T>& theList)
{
    listSize = theList.listSize;

    if(listSize == 0)
    {
        firstNode = NULL;
        listSize = 0;
        //return;
    }

    // 进行拷贝
    // 中间变量
    chainNode<T>* sourceNode = theList.firstNode;
    firstNode = new chainNode<T>(sourceNode->element);
    // 因为要保证firstNode始终指向头指正,并且能够正常赋值,
    // 采取另一个指针来拷贝
    chainNode<T>* temp = firstNode;

    sourceNode = sourceNode->next;
    while(sourceNode != NULL)
    {
        temp->next = new chainNode<T>(sourceNode->element);
        temp = temp->next;
        sourceNode = sourceNode->next;
    }

    temp->next = NULL;
}

template <typename T>
chain<T>::~chain()
{
    while(firstNode != NULL)
    {
        chainNode<T>* nextNode = firstNode->next;
        delete firstNode;
        firstNode = nextNode;
    }
}
add() 和 insert() 操作

思路都是把上一个节点的指针指向插入的元素,插入元素的指针指向节点的下一个节点
插入操作

代码实现

template <typename T>
void chain<T>::add(int theElement)
{
    if(firstNode == NULL)
    {
        firstNode = new chainNode<T>(theElement, firstNode);
    }
    else
    {
        chainNode<T>* p = firstNode;
        while(p->next != NULL)
        {
            p = p->next;
        }

        p->next = new chainNode<T>;
        p->next->element = theElement;
        p->next->next = NULL;

    }
    listSize++;
}

template <typename T>
void chain<T>::insert(int theIndex, const T& theElement)
{
    if(theIndex < 0 || theIndex > listSize)
        return;

    if(theIndex == 0)
    {
        firstNode = new chainNode<T>(theElement, firstNode);
    }
    else
    {
        chainNode<T>* p = firstNode;
        for(int i = 0; i < theIndex - 1; i++)
        {
            p = p->next;
        }
        p = new chainNode<T>(theElement, p->next);
    }
    listSize++;
}

erase() 操作

例如,有N个节点的链表,要删除第三个节点,即是遍历到第二个节点,将第二个节点的链域从原来指向第三个节点的指针,改为指向第四个节点,然后释放第三个节点即可

template <typename T>
void chain<T>::erase(int theIndex)
{
    if(!checkIndex(theIndex))
        return;

    chainNode<T>* deleteNode = firstNode;
    if(deleteNode == NULL)
    {
        deleteNode = firstNode;
        firstNode = firstNode->next;
    }
    else
    {
        chainNode<T>* p = firstNode;
        for(int i = 0; i < theIndex - 1; i++)
        {
            p = p->next;
        }
        deleteNode = p->next;
        p->next = p->next->next;
    }
    listSize--;
    delete deleteNode;
}
get() 和 indexof() 操作

在线性表中要查找一个元素的位置,必须从第一个节点出发,依次遍历每一个节点,直到你需要的节点的位置停止。因此每次遍历的时候我们都需要一个临时指针(指向firstNode节点)来替代头结点,进行遍历,以防止头结点的指向错误,导致下一次遍历失败(或者说,下一次遍历的时候不是从头结点出发,而是从你上次遍历的时候停止的地方)

template <typename T>
T& chain<T>::get(int theIndex) const
{
    chainNode<T>* currentNode = firstNode;
    for(int i = 0; i < theIndex; i++)
    {
        currentNode = currentNode->next;
    }   

    return currentNode->element;
}

template <typename T>
int chain<T>::indexof(const T& theElement) const
{
    chainNode<T>* currentNode = firstNode;
    int index;

    for(int i = 0; i < listSize; i++)
    {
        if(currentNode->element == theElement)
            break;
        currentNode = currentNode->next;
        index++;
    }

    if(currentNode == NULL)
        return -1;
    else
        return index;
}

其他

相对于单链表,双向链表和循环链表有许多不同,例如:我们如何计算它的链表长度?
这些留给下一次写吧,谢谢你的观看,如果有什么意见请提出来。

友情链接:数据结构之线性表 – 链式描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值