数据结构基础(8) --单链表的设计与实现(1)之基本操作

简介: 链表简介数组的缺点:     1.元素插入:除了在数组的末尾插入元素之外,在数组的其他任何位置插入元素都需要进行数组元素的频繁移动(插入位置之后的元素都需往后移动), 时间复杂度约为O(N);     2.

链表简介

数组的缺点:

     1.元素插入:除了在数组的末尾插入元素之外,在数组的其他任何位置插入元素都需要进行数组元素的频繁移动(插入位置之后的元素都需往后移动), 时间复杂度约为O(N);

     2.数组的删除:除了在数组的末尾删除元素之外,在数组的其他任何位置删除元素都需要进行数组元素的频繁移动(删除位置之后的元素都需往前移动), 时间复杂度也为O(N);

 

链表的特点:

   由于在链表中插入/删除元素都不需要进行数据的移位, 只需要O(1)时间完成, 因此链表适用于频繁插入与删除的情况;

   但是链表也有缺点:链表不适用于需要频繁访问的情况, 因为如果需要查询一个数据, 链表需要遍历整个数据序列, 需要的O(n)的时间, 然而由于数组支持随机访问, 因此数组只需O(1)的时间便可完成数据的访问, 因此此时数组就非常便利了!


单链表

单链表特征如图所示:


    单链表只需一个节点(首节点first)来指向该链表,有时为了操作方便,在第一个结点之前虚加一个”头结点”(算法导论称之为”哑节点”),以指向头结点的指针为链表的头指针(为了实现上的方便我们采用了这种带有附加头结点的链表实现方案同时也为将来我们将该链表改造成循环链表与循环双链表打下了基础)

    由于单链表是一种顺序存取的结构, 因此为找第 i 个数据元素, 必须先找到第 i-1 个数据元素。

 

单链表节点构造:

class Node
{
private:
    Type data;  //数据域:节点数据
    Node *next; //指针域:下一个节点
};

但为了能够应用于MyList<Type>需要对其改造:

//链表节点
template <typename Type>
class Node
{
    //可以将MyList类作为Node的友元
    //或者将Node类做成MyList的嵌套类, 嵌套在MyList中, 也可以完成该功能
friend class MyList<Type>;

    template <typename T>
    friend ostream &operator<<(ostream &os, const MyList<T> &list);
private:
    //constructor说明:
    //next = NULL;    //因为这是一个新生成的节点, 因此下一个节点为空
    Node(const Type &dataValue):data(dataValue), next(NULL) {}

    Type data;  //数据域:节点数据
    Node *next; //指针域:下一个节点
};

单链表构造:

//链表
template <typename Type>
class MyList
{
    template <typename T>
    friend ostream &operator<<(ostream &os, const MyList<T> &list);
public:
    MyList();
    ~MyList();

    //将元素插入表头
    void insertFront(const Type &data);
    //将元素插入到位置index上(index从1开始)
    void insert(const Type &data, int index);
    //删除表中所有值为data的节点
    void remove(const Type &data);
    bool isEmpty() const;

    //链表反转
    void invort();
    //将链表(list)链接到本条链表的末尾
    void concatenate(const MyList<Type> &list);

private:
    //指向第一个节点的指针
    Node<Type> *first;
};
//链表的构造
template <typename Type>
MyList<Type>::MyList()
{
    //first指向一个空节点
    first = new Node<Type>(0);
    first -> next = NULL;
}
//链表的析构
template <typename Type>
MyList<Type>::~MyList()
{
    Node<Type> *deleteNode = NULL;
    while (first != NULL)
    {
        deleteNode = first;
        first = first -> next;
        delete deleteNode;
    }
}

元素插入:

    由前面的图像可见,在单链表中插入结点只需要修改指针。但同时,若要在第i个结点之前插入元素,修改的是第i-1 个结点的指针。

    因此,在单链表中第 i 个结点要进行的基本工作为:找到线性表中第i-1个结点,然后修改其指向后继的指针。

template <typename Type>
void MyList<Type>::insertFront(const Type &data)
{
    Node<Type> *newNode = new Node<Type>(data);
    newNode -> next = first -> next;
    first -> next = newNode;
}
template <typename Type>
void MyList<Type>::insert(const Type &data, int index)
{
    //由于我们在表头添加了一个空节点
    //因此如果链表为空, 或者在链表为1的位置添加元素
    //其操作与在其他位置添加元素相同

    int count = 1;
    //此时searchNode肯定不为NULL
    Node<Type> *searchNode = first;
    // 找到要插入的位置
    // 如果所给index过大(超过了链表的长度)
    // 则将该元素插入到链表表尾
    // 原因是 searchNode->next != NULL 这个条件已经不满足了
    // 已经到达表尾
    while (count < index && searchNode->next != NULL)
    {
        ++ count;
        searchNode = searchNode->next;
    }

    // 插入链表
    Node<Type> *newNode = new Node<Type>(data);
    newNode->next = searchNode->next;
    searchNode->next = newNode;
}

元素的删除:

    在单链表中删除第 i 个结点的基本操作为:找到线性表中第i-1个结点,修改其指向后继的指针。

template <typename Type>
void MyList<Type>::remove(const Type &data)
{
    if (isEmpty())
        return ;

    Node<Type> *previous = first;   //保存要删除节点的前一个节点
    for (Node<Type> *searchNode = first->next;
            searchNode != NULL;
            searchNode = searchNode->next)
    {
        if (searchNode->data == data)
        {
            previous->next = searchNode->next;
            delete searchNode;
            //重新调整searchNode指针
            //继续遍历链表查看是否还有相等元素

            //如果当前searchNode已经到达了最后一个节点
            //也就是searchNode->next已经等于NULL了, 则下面这条语句不能执行
            if (previous->next == NULL)
                break;

            searchNode = previous->next;
        }
        previous = searchNode;
    }
}

//链表的判空
template <typename Type>
bool MyList<Type>::isEmpty() const
{
    return first->next == NULL;
}

目录
相关文章
|
29天前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
53 4
|
22天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
43 5
|
1月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
80 4
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
46 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
29天前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
47 0
|
2月前
|
存储
[数据结构] -- 单链表
[数据结构] -- 单链表
26 1
|
2月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
29 7
|
2月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
27 3